diff --git a/SpellingExclusions.dic b/SpellingExclusions.dic index 44098153565f7..17a33d838ea5c 100644 --- a/SpellingExclusions.dic +++ b/SpellingExclusions.dic @@ -2,3 +2,4 @@ awaitable Refactorings Infos +cref diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 39bf2f2c610a1..0f26c4fff3483 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -53,6 +53,12 @@ variables: ${{ else }}: value: windows.vs2022preview.amd64 + - name: Vs2022RegularQueueName + ${{ if eq(variables['System.TeamProject'], 'public') }}: + value: windows.vs2022.amd64.open + ${{ else }}: + value: windows.vs2022.amd64 + - name: UbuntuQueueName ${{ if eq(variables['System.TeamProject'], 'public') }}: value: Build.Ubuntu.2004.Amd64.Open @@ -107,6 +113,11 @@ parameters: default: name: $(PoolName) demands: ImageOverride -equals $(Vs2022PreviewQueueName) + - name: vs2022RegularPool + type: object + default: + name: $(PoolName) + demands: ImageOverride -equals $(Vs2022RegularQueueName) stages: - stage: Windows_Debug_Build @@ -271,7 +282,7 @@ stages: jobName: Test_Windows_CoreClr_Debug_Single_Machine testArtifactName: Transport_Artifacts_Windows_Debug configuration: Debug - poolParameters: ${{ parameters.vs2022PreviewPool }} + poolParameters: ${{ parameters.vs2022RegularPool }} testArguments: -testCoreClr - template: eng/pipelines/test-windows-job.yml diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md index 46ad586959a4b..6375ddd575789 100644 --- a/docs/Language Feature Status.md +++ b/docs/Language Feature Status.md @@ -6,7 +6,7 @@ and will be updated as work progresses, features are added / removed, and as wor This is not an exhaustive list of our features but rather the ones which have active development efforts behind them. -# Working Set +# Working Set C# | Feature | Branch | State | Developer | Reviewer | IDE Buddy | LDM Champ | | ------- | ------ | ----- | --------- | -------- | --------- | --------- | @@ -15,6 +15,16 @@ efforts behind them. | [Null-conditional assignment](https://github.com/dotnet/csharplang/issues/6045) | [null-conditional-assignment](https://github.com/dotnet/roslyn/tree/features/null-conditional-assignment) | [In Progress](https://github.com/dotnet/roslyn/issues/75554) | [RikkiGibson](https://github.com/RikkiGibson) | [cston](https://github.com/cston), [jjonescz](https://github.com/jjonescz) | TBD | [RikkiGibson](https://github.com/RikkiGibson) | | [`field` keyword in properties](https://github.com/dotnet/csharplang/issues/140) | [field-keyword](https://github.com/dotnet/roslyn/tree/features/field-keyword) | [Merged into 17.12p3](https://github.com/dotnet/roslyn/issues/57012) | [Youssef1313](https://github.com/Youssef1313), [cston](https://github.com/cston) | [333fred](https://github.com/333fred), [RikkiGibson](https://github.com/RikkiGibson) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | | [First-class Span Types](https://github.com/dotnet/csharplang/issues/7905) | [FirstClassSpan](https://github.com/dotnet/roslyn/tree/features/FirstClassSpan) | [Merged into 17.13p1](https://github.com/dotnet/roslyn/issues/73445) | [jjonescz](https://github.com/jjonescz) | [cston](https://github.com/cston), [333fred](https://github.com/333fred) | | [333fred](https://github.com/333fred), [stephentoub](https://github.com/stephentoub) | +| [Unbound generic types in `nameof`](https://github.com/dotnet/csharplang/issues/8480) | [PR](https://github.com/dotnet/roslyn/pull/75368) | [In Progress](https://github.com/dotnet/roslyn/pull/75368) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [jcouv](https://github.com/jcouv) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | +| Runtime Async | [features/runtime-async](https://github.com/dotnet/roslyn/tree/features/runtime-async) | [In Progress](https://github.com/dotnet/roslyn/issues/75960) | [333fred](https://github.com/333fred) | [jcouv](https://github.com/jcouv), [RikkiGibson](https://github.com/RikkiGibson) | | | + +# Working Set VB + +| Feature | Branch | State | Developer | Reviewer | IDE Buddy | LDM Champ | +| ------- | ------ | ----- | --------- | -------- | --------- | --------- | +| Recognizing 'unmanaged' constraint | main | [Merged into 17.13 P2](https://github.com/dotnet/roslyn/pull/75665) | [AlekseyTs](https://github.com/AlekseyTs) | [cston](https://github.com/cston), [333fred](https://github.com/333fred) | (no IDE impact) | [jaredpar](https://github.com/jaredpar) | +| Overload Resolution Priority | [features/VBOverloadResolutionPriority](https://github.com/dotnet/roslyn/tree/features/VBOverloadResolutionPriority) | In Progress | [AlekseyTs](https://github.com/AlekseyTs) | [333fred](https://github.com/333fred), [cston](https://github.com/cston) | (no IDE impact) | [333fred](https://github.com/333fred) | + # C# 13.0 diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md index 9debb96483dcd..b1f6e72765761 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md @@ -1,4 +1,6 @@ -# This document lists known breaking changes in Roslyn after .NET 9 all the way to .NET 10. +# Breaking changes in Roslyn after .NET 9.0.100 through .NET 10.0.100 + +This document lists known breaking changes in Roslyn after .NET 9 general release (.NET SDK version 9.0.100) through .NET 10 general release (.NET SDK version 10.0.100). ## `Span` and `ReadOnlySpan` overloads are applicable in more scenarios in C# 14 and newer diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md index 16ff18c9ad43f..6c6b64d41eb5d 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md @@ -1,4 +1,6 @@ -# This document lists known breaking changes in Roslyn after .NET 6 all the way to .NET 7. +# Breaking changes in Roslyn after .NET 6.0.100 through .NET 7.0.100 + +This document lists known breaking changes in Roslyn after .NET 6 general release (.NET SDK version 6.0.100) through .NET 7 general release (.NET SDK version 7.0.100). ## All locals of restricted types are disallowed in async methods diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md index 351e636ac80db..470853cd3c8f8 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md @@ -1,4 +1,6 @@ -# This document lists known breaking changes in Roslyn after .NET 7 all the way to .NET 8. +# Breaking changes in Roslyn after .NET 7.0.100 through .NET 8.0.100 + +This document lists known breaking changes in Roslyn after .NET 7 general release (.NET SDK version 7.0.100) through .NET 8 general release (.NET SDK version 8.0.100). ## Ref modifiers of dynamic arguments should be compatible with ref modifiers of corresponding parameters diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md index b1890cfa20293..ce0ac3ee11fe5 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md @@ -1,5 +1,6 @@ -# This document lists known breaking changes in Roslyn after .NET 8 all the way to .NET 9. +# Breaking changes in Roslyn after .NET 8.0.100 through .NET 9.0.100 +This document lists known breaking changes in Roslyn after .NET 8 general release (.NET SDK version 8.0.100) through .NET 9 general release (.NET SDK version 9.0.100). ## InlineArray attribute on a record struct type is no longer allowed. diff --git a/docs/compilers/CSharp/Runtime Async Design.md b/docs/compilers/CSharp/Runtime Async Design.md new file mode 100644 index 0000000000000..ba218eaceb8db --- /dev/null +++ b/docs/compilers/CSharp/Runtime Async Design.md @@ -0,0 +1,543 @@ +# Runtime Async Design + +See also the ECMA-335 specification change for this feature: https://github.com/dotnet/runtime/blob/main/docs/design/specs/runtime-async.md. https://github.com/dotnet/runtime/issues/109632 tracks open issues for the feature in the runtime. + +This document goes over the general design of how Roslyn works with the Runtime Async feature to produce IL. In general, we try to avoid exposing this feature at the user level; initial binding is almost entirely +unaffected by runtime async. Exposed symbols do not give direct information about whether they were compiled with runtime async, and indeed the compiler has no idea whether a method from a referenced assembly is +compiled with runtime async or not. + +## Supporting runtime apis + +We use the following runtime flag to drive whether feature can be used. This flag must be defined in the same assembly that defines `object`, and the assembly cannot reference any other assemblies. In terms of +CoreFX, this means it must be defined in the `System.Runtime` reference assembly. + +TODO: Determine whether just the presence of this flag will cause the compiler to generate in runtime async mode. + +```cs +namespace System.Runtime.CompilerServices; + +public static class RuntimeFeature +{ + public const string Async = nameof(Async); +} +``` + +We use the following helper APIs to indicate suspension points to the runtime, in addition to the runtime async call syntax: + +```cs +namespace System.Runtime.CompilerServices; + +// These methods are used to await things that cannot use runtime async signature form +// TODO: Clarify which of these should be preferred? Should we always emit the `Unsafe` version when awaiting something that implements `ICriticalNotifyCompletion`? +namespace System.Runtime.CompilerServices; + +public static class RuntimeHelpers +{ + [RuntimeAsyncMethod] + public static Task AwaitAwaiterFromRuntimeAsync(TAwaiter awaiter) where TAwaiter : INotifyCompletion; + [RuntimeAsyncMethod] + public static Task UnsafeAwaitAwaiterFromRuntimeAsync(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion; +} +``` + +Additionally, we use the following helper attributes to indicate information to the runtime. If these attributes are not present in the reference assemblies, we will generate them; the runtime matches by full +name, not by type identity, so we do not need to care about using the "canonical" versions. + +```cs +namespace System.Runtime.CompilerServices; + +// Used to tell the runtime to generate the async state machinery for this method +[AttributeUsage(AttributeTargets.Method)] +public class RuntimeAsyncMethodAttribute() : Attribute(); + +// Used to mark locals that should be hoisted to the generated async closure. Note that the runtime does not guarantee that all locals marked with this modreq will be hoisted; if it can prove that it +// doesn't need to hoist a variable, it may avoid doing so. +public class HoistedLocal(); +``` + +For experimentation purposes, we recognize an attribute that can be used to force the compiler to generate the runtime async code, or to force the compiler to generate a full state machine. This attribute is not +defined in the BCL, and exists as an escape hatch for experimentation. It may be removed when the feature ships in stable. + +```cs +namespace System.Runtime.CompilerServices; + +[AttributeUsage(AttributeTargets.Method)] +public class RuntimeAsyncMethodGenerationAttribute(bool runtimeAsync) : Attribute(); +``` + +## Transformation strategy + +As mentioned previously, we try to expose as little of this to initial binding as possible. The one major exception to this is our handling of the `RuntimeAsyncMethodAttribute`; we do not let this be applied to +user code, and will issue an error if a user tries to do this by hand. + +Compiler generated async state machines and runtime generated async share some of the same building blocks. Both need to have `await`s with in `catch` and `finally` blocks rewritten to pend the exceptions, +perform the `await` outside of the `catch`/`finally` region, and then have the exceptions restored as necessary. + +TODO: Go over `IAsyncEnumerable` and confirm that the initial rewrite to a `Task`-based method produces code that can then be implemented with runtime async, rather than a full compiler state machine. + +TODO: Clarify with the debugger team where NOPs need to be inserted for debugging/ENC scenarios. + We will likely need to insert AwaitYieldPoint and AwaitResumePoints for the scenarios where we emit calls to `RuntimeHelpers` async helpers, but can we avoid them for calls in runtime async form? + +TODO: Do we need to implement clearing of locals marked with `Hoisted`, or will the runtime handle that? + +### Example transformations + +Below are some examples of what IL is generated for specific examples. + +TODO: Include debug versions + +#### General signature transformation + +In general, an async method declared in C# will be transformed as follows: + +```cs +async Task M() +{ + // ... +} +``` + +```cs +[RuntimeAsyncMethod, Experimental] +Task M() +{ + // ... see lowering strategy for each kind of await below ... +} +``` + +The same holds for methods that return `Task`, `ValueTask`, and `ValueTask`. Any method returning a different `Task`-like type is not transformed to runtime async form and uses a C#-generated state machine. + +`await`s within the body will either be transformed to Runtime-Async call format (as detailed in the runtime specification), or we will use one of the `RuntimeHelpers` methods to do the `await`. Specifics +for given scenarios are elaborated in more detail below. + +`Experimental` will be removed when the full feature is ready to ship, likely not before .NET 11. + +TODO: Async iterators (returning `IAsyncEnumerable`) + +#### Await `Task`-returning method + +```cs +class C +{ + static Task M(); +} + +await C.M(); +``` + +```il +call modreq(class [System.Runtime]System.Threading.Tasks.Task) void C::M() +``` + +--------------------------- + +```cs +var c = new C(); +await c.M(); + +class C +{ + Task M(); +} +``` + +```il +newobj instance void C::.ctor() +callvirt instance modreq(class [System.Runtime]System.Threading.Tasks.Task) void C::M() +``` + +#### Await a concrete `T` `Task`-returning method + +```cs +int i = await C.M(); + +class C +{ + static Task M(); +} +``` + +```il +call modreq(class [System.Runtime]System.Threading.Tasks.Task`1) int32 C::M() +stloc.0 +``` + +--------------------------- + +```cs +var c = new C(); +int i = await c.M(); + +class C +{ + Task M(); +} +``` + +```il +newobj instance void C::.ctor() +callvirt instance modreq(class [System.Runtime]System.Threading.Tasks.Task`1) int32 C::M() +stloc.0 +``` + +#### Await local of type `Task` + +```cs +var local = M(); +await local; + +class C +{ + static Task M(); +} +``` + +Translated C#: + +```cs +var local = C.M(); +{ + var awaiter = local.GetAwaiter(); + if (!awaiter.IsComplete) + { + /* Runtime-Async Call */ System.Runtime.CompilerServices.RuntimeHelpers.AwaitAwaiterFromRuntimeAsync(awaiter); + } + awaiter.GetResult() +} +``` + +```il +{ + .locals init ( + [0] valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter awaiter + ) + + IL_0000: call class [System.Runtime]System.Threading.Tasks.Task C::M() + IL_0005: callvirt instance valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter [System.Runtime]System.Threading.Tasks.Task::GetAwaiter() + IL_000a: stloc.0 + IL_000b: ldloca.s 0 + IL_000d: call instance bool [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter::get_IsCompleted() + IL_0012: brtrue.s IL_001b + + IL_0014: ldloc.0 + IL_0015: call class [System.Runtime]System.Threading.Tasks.Task System.Runtime.CompilerServices.RuntimeHelpers::AwaitAwaiterFromRuntimeAsync(!!0) + IL_001a: pop + + IL_001b: ldloca.s 0 + IL_001d: call instance void [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter::GetResult() + IL_0022: ret +} +``` + +#### Await local of concrete type `Task` + +This strategy will also be used for `Task`-like types that are not `Task`, `ValueTask`, `Task`, or `ValueTask`, both in value form, and in direct method call form. We will use either `AwaitAwaiterFromRuntimeAsync` or +`UnsafeAwaitAwaiterFromRuntimeAsync`, depending on the interface implemented by the custom awaitable. + +```cs +var local = M(); +var i = await local; + +class C +{ + static Task M(); +} +``` + +Translated C#: + +```cs +var local = C.M(); +var i = +{ + var awaiter = local.GetAwaiter(); + if (!awaiter.IsComplete) + { + /* Runtime-Async Call */ System.Runtime.CompilerServices.RuntimeHelpers.AwaitAwaiterFromRuntimeAsync(awaiter); + } + awaiter.GetResult() +}; +``` + +```il +{ + .locals init ( + [0] valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1 awaiter + ) + + IL_0000: call class [System.Runtime]System.Threading.Tasks.Task`1 C::M() + IL_0005: callvirt instance valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1 class [System.Runtime]System.Threading.Tasks.Task`1::GetAwaiter() + IL_000a: stloc.0 + IL_000b: ldloca.s 0 + IL_000d: call instance bool valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1::get_IsCompleted() + IL_0012: brtrue.s IL_001b + + IL_0014: ldloc.0 + IL_0015: call class [System.Runtime]System.Threading.Tasks.Task System.Runtime.CompilerServices.RuntimeHelpers::AwaitAwaiterFromRuntimeAsync>(!!0) + IL_001a: pop + + IL_001b: ldloca.s 0 + IL_001d: call instance !0 valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1::GetResult() + IL_0022: pop + IL_0023: ret +} +``` + +#### Await a `T`-returning method + +```cs +await C.M(); + +class C +{ + static T M(); +} +``` + +```il +TODO: https://github.com/dotnet/runtime/issues/109632 +``` + +#### Await a generic `T` `Task`-returning method + +```cs +await C.M(); + +class C +{ + static Task M(); +} +``` + +```il +TODO: https://github.com/dotnet/runtime/issues/109632 +``` + +#### Await a `Task`-returning delegate + +```cs +AsyncDelegate d = C.M; +await d(); + +delegate Task AsyncDelegate(); + +class C +{ + static Task M(); +} +``` + +```il +TODO: https://github.com/dotnet/runtime/issues/109632 +``` + +#### Await a `T`-returning delegate + +```cs +Func d = C.M; +await d(); + +class C +{ + static Task M(); +} +``` + +```il +TODO: https://github.com/dotnet/runtime/issues/109632 +``` + +#### Awaiting in a `catch` block + +```cs +try +{ + throw new Exception(); +} +catch (Exception ex) +{ + await C.M(); + throw; +} + +class C +{ + static Task M(); +} +``` + +Translated C#: + +```cs +int pendingCatch = 0; +Exception pendingException; +try +{ + throw new Exception(); +} +catch (Exception e) +{ + pendingCatch = 1; + pendingException = e; +} + +if (pendingCatch == 1) +{ + /* Runtime-Async Call */ C.M(); + throw pendingException; +} +``` + +```il +{ + .locals init ( + [0] int32 pendingCatch, + [1] class [System.Runtime]System.Exception pendingException + ) + + .try + { + IL_0000: newobj instance void [System.Runtime]System.Exception::.ctor() + IL_0005: throw + } + catch [System.Runtime]System.Exception + { + IL_0006: stloc.1 + IL_0007: ldc.i4.1 + IL_0008: stloc.0 + IL_0009: leave.s IL_000b + } + + IL_000b: ldloc.0 + IL_000c: ldc.i4.1 + IL_000d: bne.un.s IL_0017 + + IL_000f: ldloc.1 + IL_0010: call modreq(class [System.Runtime]System.Threading.Tasks.Task) void C::M() + IL_0015: pop + IL_0016: throw + + IL_0017: ret +} +``` + +#### Awaiting in a `finally` block + +```cs +try +{ + throw new Exception(); +} +finally +{ + await C.M(); +} + +class C +{ + static Task M(); +} +``` + +Translated C#: + +```cs +Exception pendingException; +try +{ + throw new Exception(); +} +catch (Exception e) +{ + pendingException = e; +} + +/* Runtime-Async Call */ C.M(); + +if (pendingException != null) +{ + throw pendingException; +} +``` + +```il +{ + .locals init ( + [0] class [System.Runtime]System.Exception pendingException + ) + + .try + { + IL_0000: newobj instance void [System.Runtime]System.Exception::.ctor() + IL_0005: throw + } + catch [System.Runtime]System.Exception + { + IL_0006: stloc.0 + IL_0007: leave.s IL_0009 + } + + IL_0009: call modreq(class [System.Runtime]System.Threading.Tasks.Task) void C::M() + IL_000e: pop + IL_000f: ldloc.0 + IL_0010: brfalse.s IL_0014 + + IL_0012: ldloc.0 + IL_0013: throw + + IL_0014: ret +} +``` + +#### Preserving compound assignments + +```cs +int[] a = new int[] { }; +a[C.M2()] += await C.M1(); + +class C +{ + public static Task M1(); + public static int M2(); +} +``` + +Translated C#: + +```cs +int[] a = new int[] { }; +int _tmp1 = C.M2(); +int _tmp2 = a[_tmp1]; +int _tmp3 = /* Runtime-Async Call */ C.M1(); +a[_tmp1] = _tmp2 + _tmp3; +``` + +```il +{ + .locals init ( + [0] int32[] a, + [1] int32 _tmp1, + [2] int32 _tmp2, + [3] int32 _tmp3 + ) + + IL_0000: ldc.i4.0 + IL_0001: newarr [System.Runtime]System.Int32 + IL_0006: stloc.0 + IL_0007: call int32 C::M2() + IL_000c: stloc.1 + IL_000d: ldloc.0 + IL_000e: ldloc.1 + IL_000f: ldelem.i4 + IL_0010: stloc.2 + IL_0011: call modreq(class [System.Runtime]System.Threading.Tasks.Task) int32 C::M1() + IL_0016: stloc.3 + IL_0017: ldloc.0 + IL_0018: ldloc.1 + IL_0019: ldloc.2 + IL_001a: ldloc.3 + IL_001b: add + IL_001c: stelem.i4 + IL_001d: ret +} +``` diff --git a/eng/Directory.Packages.props b/eng/Directory.Packages.props index 4fbc6c58e26cf..1466d9260850b 100644 --- a/eng/Directory.Packages.props +++ b/eng/Directory.Packages.props @@ -8,7 +8,7 @@ <_BasicReferenceAssembliesVersion>1.7.9 4.8.0-3.final - 17.10.191 + 17.12.145-preview 9.0.0-rc.2.24462.10 6.0.0-rtm.21518.12 7.0.0-alpha.1.22060.1 @@ -22,7 +22,6 @@ 8.0.10 <_xunitVersion>2.6.6 2.1.0 - 17.10.2079 - + - - - + + + @@ -91,39 +90,40 @@ - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - + + + + - - - + + + - + https://github.com/dotnet/source-build-externals - 4df883d781a4290873b3b968afc0ff0df7132507 + c65b1c1affed1f4847f9c3f81623dfa929d21e1a - + https://github.com/dotnet/source-build-reference-packages - ccd0927e3823fb178c7151594f5d2eaba81bba81 + df6fdaf26c24fa7c897ca8062227aa28e76e2339 - + https://github.com/dotnet/command-line-api - 803d8598f98fb4efd94604b32627ee9407f246db + feb61c7f328a2401d74f4317b39d02126cfdfe24 - + https://github.com/dotnet/command-line-api - 803d8598f98fb4efd94604b32627ee9407f246db + feb61c7f328a2401d74f4317b39d02126cfdfe24 @@ -122,14 +122,14 @@ - + https://github.com/dotnet/arcade - 3c393bbd85ae16ddddba20d0b75035b0c6f1a52d + 1c7e09a8d9c9c9b15ba574cd6a496553505559de - + https://github.com/dotnet/arcade - 3c393bbd85ae16ddddba20d0b75035b0c6f1a52d + 1c7e09a8d9c9c9b15ba574cd6a496553505559de @@ -156,9 +156,9 @@ https://github.com/dotnet/roslyn 5d10d428050c0d6afef30a072c4ae68776621877 - + https://github.com/dotnet/arcade - 3c393bbd85ae16ddddba20d0b75035b0c6f1a52d + 1c7e09a8d9c9c9b15ba574cd6a496553505559de https://github.com/dotnet/roslyn-analyzers diff --git a/eng/Versions.props b/eng/Versions.props index be8b3a8265310..5ce59ad87b5fb 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -8,7 +8,7 @@ 4 13 0 - 1 + 2 $(MajorVersion).$(MinorVersion).$(PatchVersion) - 2.0.0-beta4.24324.3 + 2.0.0-beta4.24528.1 8.0.0 8.0.0 8.0.0 8.0.0 8.0.0 - 8.0.0 + 8.0.1 8.0.0 8.0.0 8.0.0 diff --git a/eng/common/templates-official/steps/get-delegation-sas.yml b/eng/common/templates-official/steps/get-delegation-sas.yml index 6e368af1ce310..c5a9c1f8275c5 100644 --- a/eng/common/templates-official/steps/get-delegation-sas.yml +++ b/eng/common/templates-official/steps/get-delegation-sas.yml @@ -1,52 +1,7 @@ -parameters: -- name: federatedServiceConnection - type: string -- name: outputVariableName - type: string -- name: expiryInHours - type: number - default: 1 -- name: base64Encode - type: boolean - default: false -- name: storageAccount - type: string -- name: container - type: string -- name: permissions - type: string - default: 'rl' - steps: -- task: AzureCLI@2 - displayName: 'Generate delegation SAS Token for ${{ parameters.storageAccount }}/${{ parameters.container }}' - inputs: - azureSubscription: ${{ parameters.federatedServiceConnection }} - scriptType: 'pscore' - scriptLocation: 'inlineScript' - inlineScript: | - # Calculate the expiration of the SAS token and convert to UTC - $expiry = (Get-Date).AddHours(${{ parameters.expiryInHours }}).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") - - # Temporarily work around a helix issue where SAS tokens with / in them will cause incorrect downloads - # of correlation payloads. https://github.com/dotnet/dnceng/issues/3484 - $sas = "" - do { - $sas = az storage container generate-sas --account-name ${{ parameters.storageAccount }} --name ${{ parameters.container }} --permissions ${{ parameters.permissions }} --expiry $expiry --auth-mode login --as-user -o tsv - if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to generate SAS token." - exit 1 - } - } while($sas.IndexOf('/') -ne -1) - - if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to generate SAS token." - exit 1 - } - - if ('${{ parameters.base64Encode }}' -eq 'true') { - $sas = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($sas)) - } +- template: /eng/common/core-templates/steps/get-delegation-sas.yml + parameters: + is1ESPipeline: true - Write-Host "Setting '${{ parameters.outputVariableName }}' with the access token value" - Write-Host "##vso[task.setvariable variable=${{ parameters.outputVariableName }};issecret=true]$sas" \ No newline at end of file + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/get-delegation-sas.yml b/eng/common/templates/steps/get-delegation-sas.yml index 6e368af1ce310..83760c9798e36 100644 --- a/eng/common/templates/steps/get-delegation-sas.yml +++ b/eng/common/templates/steps/get-delegation-sas.yml @@ -1,52 +1,7 @@ -parameters: -- name: federatedServiceConnection - type: string -- name: outputVariableName - type: string -- name: expiryInHours - type: number - default: 1 -- name: base64Encode - type: boolean - default: false -- name: storageAccount - type: string -- name: container - type: string -- name: permissions - type: string - default: 'rl' - steps: -- task: AzureCLI@2 - displayName: 'Generate delegation SAS Token for ${{ parameters.storageAccount }}/${{ parameters.container }}' - inputs: - azureSubscription: ${{ parameters.federatedServiceConnection }} - scriptType: 'pscore' - scriptLocation: 'inlineScript' - inlineScript: | - # Calculate the expiration of the SAS token and convert to UTC - $expiry = (Get-Date).AddHours(${{ parameters.expiryInHours }}).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") - - # Temporarily work around a helix issue where SAS tokens with / in them will cause incorrect downloads - # of correlation payloads. https://github.com/dotnet/dnceng/issues/3484 - $sas = "" - do { - $sas = az storage container generate-sas --account-name ${{ parameters.storageAccount }} --name ${{ parameters.container }} --permissions ${{ parameters.permissions }} --expiry $expiry --auth-mode login --as-user -o tsv - if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to generate SAS token." - exit 1 - } - } while($sas.IndexOf('/') -ne -1) - - if ($LASTEXITCODE -ne 0) { - Write-Error "Failed to generate SAS token." - exit 1 - } - - if ('${{ parameters.base64Encode }}' -eq 'true') { - $sas = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($sas)) - } +- template: /eng/common/core-templates/steps/get-delegation-sas.yml + parameters: + is1ESPipeline: false - Write-Host "Setting '${{ parameters.outputVariableName }}' with the access token value" - Write-Host "##vso[task.setvariable variable=${{ parameters.outputVariableName }};issecret=true]$sas" \ No newline at end of file + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/config/PublishData.json b/eng/config/PublishData.json index feb600481132f..a621e8447545d 100644 --- a/eng/config/PublishData.json +++ b/eng/config/PublishData.json @@ -215,6 +215,15 @@ "vsMajorVersion": 17, "insertionTitlePrefix": "[d17.12]" }, + "release/dev17.13": { + "nugetKind": [ + "Shipping", + "NonShipping" + ], + "vsBranch": "rel/d17.13", + "vsMajorVersion": 17, + "insertionTitlePrefix": "[d17.13 P1]" + }, "main": { "nugetKind": [ "Shipping", @@ -223,7 +232,7 @@ "vsBranch": "main", "vsMajorVersion": 17, "insertionCreateDraftPR": false, - "insertionTitlePrefix": "[d17.13 P1]" + "insertionTitlePrefix": "[d17.13 P2]" }, "dev/andrha/telemetry": { "nugetKind": [ diff --git a/eng/pipelines/test-integration-helix.yml b/eng/pipelines/test-integration-helix.yml index 3ac4f33fcc055..9cb3fefdfaa33 100644 --- a/eng/pipelines/test-integration-helix.yml +++ b/eng/pipelines/test-integration-helix.yml @@ -69,9 +69,9 @@ stages: # This is a temporary step until the actual test run moves to helix (then we would need to rehydrate the tests there instead) - task: BatchScript@1 - displayName: Rehydrate Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests (net8.0) + displayName: Rehydrate Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests (net9.0) inputs: - filename: ./artifacts\bin\Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests/${{ parameters.configuration }}/net8.0/rehydrate.cmd + filename: ./artifacts\bin\Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests/${{ parameters.configuration }}/net9.0/rehydrate.cmd env: HELIX_CORRELATION_PAYLOAD: '$(Build.SourcesDirectory)\.duplicate' diff --git a/eng/pipelines/test-windows-job-single-machine.yml b/eng/pipelines/test-windows-job-single-machine.yml index 75acc4af38da4..975bdb744f2f0 100644 --- a/eng/pipelines/test-windows-job-single-machine.yml +++ b/eng/pipelines/test-windows-job-single-machine.yml @@ -33,7 +33,7 @@ jobs: displayName: 'Install .NET 9 Runtime' inputs: packageType: runtime - version: 9.0.0-preview.3.24172.9 + version: 9.0.0-rc.2.24473.5 includePreviewVersions: true installationPath: '$(Build.SourcesDirectory)/.dotnet' diff --git a/global.json b/global.json index a7c4604b7c964..d0270ea4b9171 100644 --- a/global.json +++ b/global.json @@ -11,8 +11,8 @@ } }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24516.2", - "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24516.2", + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24562.13", + "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24562.13", "Microsoft.Build.Traversal": "3.4.0" } } diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems index 5f550c427c2b5..ccb4024aa4b05 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems @@ -83,6 +83,7 @@ + @@ -154,6 +155,7 @@ + diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx index 5fe66d4bd5183..1574a4e49a86a 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx @@ -422,4 +422,7 @@ Use 'System.Threading.Lock' {Locked="System.Threading.Lock"} "System.Threading.Lock" is the name of a .Net type and should not be localized. + + Use unbound generic type + \ No newline at end of file diff --git a/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs b/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs index 0bd7b8598310b..a2fc11e8f70c3 100644 --- a/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs +++ b/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs @@ -69,6 +69,7 @@ internal CSharpSimplifierOptions GetSimplifierOptions() public CodeStyleOption2 PreferMethodGroupConversion => GetOption(CSharpCodeStyleOptions.PreferMethodGroupConversion); public CodeStyleOption2 PreferPrimaryConstructors => GetOption(CSharpCodeStyleOptions.PreferPrimaryConstructors); public CodeStyleOption2 PreferSystemThreadingLock => GetOption(CSharpCodeStyleOptions.PreferSystemThreadingLock); + public CodeStyleOption2 PreferUnboundGenericTypeInNameOf => GetOption(CSharpCodeStyleOptions.PreferUnboundGenericTypeInNameOf); // CodeGenerationOptions diff --git a/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer.cs index 089f5c0521045..cd7c2b26446f1 100644 --- a/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; @@ -26,18 +25,15 @@ namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryLambdaExpression; /// time. /// [DiagnosticAnalyzer(LanguageNames.CSharp)] -internal sealed class CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer : AbstractBuiltInUnnecessaryCodeStyleDiagnosticAnalyzer +internal sealed class CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer() + : AbstractBuiltInUnnecessaryCodeStyleDiagnosticAnalyzer( + IDEDiagnosticIds.RemoveUnnecessaryLambdaExpressionDiagnosticId, + EnforceOnBuildValues.RemoveUnnecessaryLambdaExpression, + CSharpCodeStyleOptions.PreferMethodGroupConversion, + fadingOption: null, + new LocalizableResourceString(nameof(CSharpAnalyzersResources.Remove_unnecessary_lambda_expression), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)), + new LocalizableResourceString(nameof(CSharpAnalyzersResources.Lambda_expression_can_be_removed), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) { - public CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer() - : base(IDEDiagnosticIds.RemoveUnnecessaryLambdaExpressionDiagnosticId, - EnforceOnBuildValues.RemoveUnnecessaryLambdaExpression, - CSharpCodeStyleOptions.PreferMethodGroupConversion, - fadingOption: null, - new LocalizableResourceString(nameof(CSharpAnalyzersResources.Remove_unnecessary_lambda_expression), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)), - new LocalizableResourceString(nameof(CSharpAnalyzersResources.Lambda_expression_can_be_removed), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) - { - } - public override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; @@ -51,7 +47,7 @@ protected override void InitializeWorker(AnalysisContext context) var conditionalAttributeType = context.Compilation.ConditionalAttribute(); context.RegisterSyntaxNodeAction( - c => AnalyzeSyntax(c, expressionType, conditionalAttributeType), + context => AnalyzeSyntax(context, expressionType, conditionalAttributeType), SyntaxKind.SimpleLambdaExpression, SyntaxKind.ParenthesizedLambdaExpression, SyntaxKind.AnonymousMethodExpression); } }); diff --git a/src/Analyzers/CSharp/Analyzers/RemoveUnusedMembers/CSharpRemoveUnusedMembersDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/RemoveUnusedMembers/CSharpRemoveUnusedMembersDiagnosticAnalyzer.cs index 55ed47da767b2..d0c61fa9d6eb2 100644 --- a/src/Analyzers/CSharp/Analyzers/RemoveUnusedMembers/CSharpRemoveUnusedMembersDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/RemoveUnusedMembers/CSharpRemoveUnusedMembersDiagnosticAnalyzer.cs @@ -7,6 +7,7 @@ using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.RemoveUnusedMembers; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -20,6 +21,8 @@ internal sealed class CSharpRemoveUnusedMembersDiagnosticAnalyzer TypeDeclarationSyntax, MemberDeclarationSyntax> { + protected override ISemanticFacts SemanticFacts => CSharpSemanticFacts.Instance; + protected override IEnumerable GetTypeDeclarations(INamedTypeSymbol namedType, CancellationToken cancellationToken) { return namedType.DeclaringSyntaxReferences diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs index de116646207c7..8ce1cd87c339a 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs @@ -20,11 +20,6 @@ internal sealed partial class CSharpUseCollectionExpressionForCreateDiagnosticAn IDEDiagnosticIds.UseCollectionExpressionForCreateDiagnosticId, EnforceOnBuildValues.UseCollectionExpressionForCreate) { - public const string UnwrapArgument = nameof(UnwrapArgument); - - private static readonly ImmutableDictionary s_unwrapArgumentProperties = - ImmutableDictionary.Empty.Add(UnwrapArgument, UnwrapArgument); - protected override void InitializeWorker(CodeBlockStartAnalysisContext context, INamedTypeSymbol? expressionType) => context.RegisterSyntaxNodeAction(context => AnalyzeInvocationExpression(context, expressionType), SyntaxKind.InvocationExpression); @@ -40,7 +35,7 @@ private void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context, INam return; var invocationExpression = (InvocationExpressionSyntax)context.Node; - if (!IsCollectionFactoryCreate(semanticModel, invocationExpression, out var memberAccess, out var unwrapArgument, cancellationToken)) + if (!IsCollectionFactoryCreate(semanticModel, invocationExpression, out var memberAccess, out var unwrapArgument, out var useSpread, cancellationToken)) return; // Make sure we can actually use a collection expression in place of the full invocation. @@ -52,9 +47,7 @@ private void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context, INam } var locations = ImmutableArray.Create(invocationExpression.GetLocation()); - var properties = unwrapArgument ? s_unwrapArgumentProperties : ImmutableDictionary.Empty; - if (changesSemantics) - properties = properties.Add(UseCollectionInitializerHelpers.ChangesSemanticsName, ""); + var properties = GetDiagnosticProperties(unwrapArgument, useSpread, changesSemantics); context.ReportDiagnostic(DiagnosticHelper.Create( Descriptor, diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForFluentDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForFluentDiagnosticAnalyzer.cs index 42f9408bd91ca..721d79add95a5 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForFluentDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForFluentDiagnosticAnalyzer.cs @@ -63,17 +63,6 @@ internal sealed partial class CSharpUseCollectionExpressionForFluentDiagnosticAn nameof(System.Collections.Immutable), ]; - /// - /// Set of type-names that are blocked from moving over to collection expressions because the semantics of them are - /// known to be specialized, and thus could change semantics in undesirable ways if the compiler emitted its own - /// code as an replacement. - /// - private static readonly ImmutableHashSet s_bannedTypes = [ - nameof(ParallelEnumerable), - nameof(ParallelQuery), - // Special internal runtime interface that is optimized for fast path conversions of collections. - "IIListProvider"]; - protected override void InitializeWorker(CodeBlockStartAnalysisContext context, INamedTypeSymbol? expressionType) => context.RegisterSyntaxNodeAction(context => AnalyzeMemberAccess(context, expressionType), SyntaxKind.SimpleMemberAccessExpression); @@ -272,12 +261,12 @@ private static bool AnalyzeInvocation( // Forms like `ImmutableArray.Create(...)` or `ImmutableArray.CreateRange(...)` are fine base cases. if (current is InvocationExpressionSyntax currentInvocationExpression && - IsCollectionFactoryCreate(semanticModel, currentInvocationExpression, out var factoryMemberAccess, out var unwrapArgument, cancellationToken)) + IsCollectionFactoryCreate(semanticModel, currentInvocationExpression, out var factoryMemberAccess, out var unwrapArgument, out var useSpread, cancellationToken)) { if (!IsListLike(current)) return false; - AddArgumentsInReverse(postMatchesInReverse, GetArguments(currentInvocationExpression, unwrapArgument), useSpread: false); + AddArgumentsInReverse(postMatchesInReverse, GetArguments(currentInvocationExpression.ArgumentList, unwrapArgument), useSpread); return true; } @@ -292,7 +281,7 @@ private static bool AnalyzeInvocation( // Down to some final collection. Like `x` in `x.Concat(y).ToArray()`. If `x` is itself is something that // can be iterated, we can convert this to `[.. x, .. y]`. Note: we only want to do this if ending with one // of the ToXXX Methods. If we just have `x.AddRange(y)` it's preference to keep that, versus `[.. x, ..y]` - if (!isAdditionMatch && IsIterable(current)) + if (!isAdditionMatch && IsIterable(semanticModel, current, cancellationToken)) { AddFinalMatch(current); return true; @@ -341,36 +330,8 @@ bool IsListLike(ExpressionSyntax expression) return false; return - Implements(type, compilation.IListOfTType()) || - Implements(type, compilation.IReadOnlyListOfTType()); - } - - bool IsIterable(ExpressionSyntax expression) - { - var type = semanticModel.GetTypeInfo(expression, cancellationToken).Type; - if (type is null or IErrorTypeSymbol) - return false; - - if (s_bannedTypes.Contains(type.Name)) - return false; - - return Implements(type, compilation.IEnumerableOfTType()) || - type.Equals(compilation.SpanOfTType()) || - type.Equals(compilation.ReadOnlySpanOfTType()); - } - - static bool Implements(ITypeSymbol type, INamedTypeSymbol? interfaceType) - { - if (interfaceType != null) - { - foreach (var baseInterface in type.AllInterfaces) - { - if (interfaceType.Equals(baseInterface.OriginalDefinition)) - return true; - } - } - - return false; + EqualsOrImplements(type, compilation.IListOfTType()) || + EqualsOrImplements(type, compilation.IReadOnlyListOfTType()); } static bool IsLegalInitializer(InitializerExpressionSyntax? initializer) @@ -426,12 +387,12 @@ private static bool IsMatch( // Check to make sure we're not calling something banned because it would change semantics. First check if the // method itself comes from a banned type (like with an extension method). var member = state.SemanticModel.GetSymbolInfo(memberAccess, cancellationToken).Symbol; - if (s_bannedTypes.Contains(member?.ContainingType.Name)) + if (BannedTypes.Contains(member?.ContainingType.Name)) return false; // Next, check if we're invoking this on a banned type. var type = state.SemanticModel.GetTypeInfo(memberAccess.Expression, cancellationToken).Type; - if (s_bannedTypes.Contains(type?.Name)) + if (BannedTypes.Contains(type?.Name)) return false; return true; diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..a0896323b7fcd --- /dev/null +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Shared.CodeStyle; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.UseCollectionInitializer; + +namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; + +using static UseCollectionExpressionHelpers; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +internal sealed partial class CSharpUseCollectionExpressionForNewDiagnosticAnalyzer() + : AbstractCSharpUseCollectionExpressionDiagnosticAnalyzer( + IDEDiagnosticIds.UseCollectionExpressionForNewDiagnosticId, + EnforceOnBuildValues.UseCollectionExpressionForNew) +{ + protected override void InitializeWorker(CodeBlockStartAnalysisContext context, INamedTypeSymbol? expressionType) + { + context.RegisterSyntaxNodeAction(context => AnalyzeObjectCreationExpression(context, expressionType), SyntaxKind.ObjectCreationExpression); + context.RegisterSyntaxNodeAction(context => AnalyzeImplicitObjectCreationExpression(context, expressionType), SyntaxKind.ImplicitObjectCreationExpression); + } + + private void AnalyzeObjectCreationExpression(SyntaxNodeAnalysisContext context, INamedTypeSymbol? expressionType) + => AnalyzeBaseObjectCreationExpression(context, (BaseObjectCreationExpressionSyntax)context.Node, expressionType); + + private void AnalyzeImplicitObjectCreationExpression(SyntaxNodeAnalysisContext context, INamedTypeSymbol? expressionType) + => AnalyzeBaseObjectCreationExpression(context, (BaseObjectCreationExpressionSyntax)context.Node, expressionType); + + private void AnalyzeBaseObjectCreationExpression( + SyntaxNodeAnalysisContext context, BaseObjectCreationExpressionSyntax objectCreationExpression, INamedTypeSymbol? expressionType) + { + var semanticModel = context.SemanticModel; + var compilation = semanticModel.Compilation; + var syntaxTree = semanticModel.SyntaxTree; + var cancellationToken = context.CancellationToken; + + if (objectCreationExpression is not { ArgumentList.Arguments: [var argument], Initializer: null }) + return; + + // no point in analyzing if the option is off. + var option = context.GetAnalyzerOptions().PreferCollectionExpression; + if (option.Value is CollectionExpressionPreference.Never || ShouldSkipAnalysis(context, option.Notification)) + return; + + var symbol = semanticModel.GetSymbolInfo(objectCreationExpression, cancellationToken).Symbol; + if (symbol is not IMethodSymbol { MethodKind: MethodKind.Constructor, Parameters: [var parameter] } || + parameter.Type.Name != nameof(IEnumerable)) + { + return; + } + + if (!Equals(compilation.IEnumerableOfTType(), parameter.Type.OriginalDefinition)) + return; + + if (!IsArgumentCompatibleWithIEnumerableOfT(semanticModel, argument, out var unwrapArgument, out var useSpread, cancellationToken)) + return; + + // Make sure we can actually use a collection expression in place of the full invocation. + var allowSemanticsChange = option.Value is CollectionExpressionPreference.WhenTypesLooselyMatch; + if (!CanReplaceWithCollectionExpression( + semanticModel, objectCreationExpression, expressionType, isSingletonInstance: false, allowSemanticsChange, skipVerificationForReplacedNode: true, cancellationToken, out var changesSemantics)) + { + return; + } + + var locations = ImmutableArray.Create(objectCreationExpression.GetLocation()); + var properties = GetDiagnosticProperties(unwrapArgument, useSpread, changesSemantics); + + context.ReportDiagnostic(DiagnosticHelper.Create( + Descriptor, + objectCreationExpression.NewKeyword.GetLocation(), + option.Notification, + context.Options, + additionalLocations: locations, + properties)); + + var additionalUnnecessaryLocations = ImmutableArray.Create( + syntaxTree.GetLocation(TextSpan.FromBounds( + objectCreationExpression.SpanStart, + objectCreationExpression.ArgumentList.OpenParenToken.Span.End)), + objectCreationExpression.ArgumentList.CloseParenToken.GetLocation()); + + context.ReportDiagnostic(DiagnosticHelper.CreateWithLocationTags( + UnnecessaryCodeDescriptor, + additionalUnnecessaryLocations[0], + NotificationOption2.ForSeverity(UnnecessaryCodeDescriptor.DefaultSeverity), + context.Options, + additionalLocations: locations, + additionalUnnecessaryLocations: additionalUnnecessaryLocations, + properties)); + } +} diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs index f7fab29e21d2e..ae7567fdb9e89 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs @@ -18,6 +18,7 @@ using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UseCollectionExpression; +using Microsoft.CodeAnalysis.UseCollectionInitializer; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; @@ -27,8 +28,22 @@ namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; internal static class UseCollectionExpressionHelpers { + public const string UnwrapArgument = nameof(UnwrapArgument); + public const string UseSpread = nameof(UseSpread); + private static readonly CollectionExpressionSyntax s_emptyCollectionExpression = CollectionExpression(); + /// + /// Set of type-names that are blocked from moving over to collection expressions because the semantics of them are + /// known to be specialized, and thus could change semantics in undesirable ways if the compiler emitted its own + /// code as an replacement. + /// + public static readonly ImmutableHashSet BannedTypes = [ + nameof(ParallelEnumerable), + nameof(ParallelQuery), + // Special internal runtime interface that is optimized for fast path conversions of collections. + "IIListProvider"]; + private static readonly SymbolEquivalenceComparer s_tupleNamesCanDifferComparer = SymbolEquivalenceComparer.Create( // Not relevant. We are not comparing method signatures. distinguishRefFromOut: true, @@ -81,7 +96,12 @@ public static bool CanReplaceWithCollectionExpression( var parent = topMostExpression.GetRequiredParent(); - if (!IsInTargetTypedLocation(semanticModel, topMostExpression, cancellationToken)) + var targetType = topMostExpression.GetTargetType(semanticModel, cancellationToken); + if (targetType is null or IErrorTypeSymbol) + return false; + + // (X[])[1, 2, 3] is target typed. `(X)[1, 2, 3]` is currently not (because it looks like indexing into an expr). + if (topMostExpression.Parent is CastExpressionSyntax castExpression && castExpression.Type is IdentifierNameSyntax) return false; // X[] = new Y[] { 1, 2, 3 } @@ -598,118 +618,6 @@ bool IsPrimitiveConstant(ExpressionSyntax expression) semanticModel.GetTypeInfo(expression, cancellationToken).Type?.IsValueType == true; } - private static bool IsInTargetTypedLocation(SemanticModel semanticModel, ExpressionSyntax expression, CancellationToken cancellationToken) - { - var topExpression = expression.WalkUpParentheses(); - var parent = topExpression.Parent; - return parent switch - { - EqualsValueClauseSyntax equalsValue => IsInTargetTypedEqualsValueClause(equalsValue), - CastExpressionSyntax castExpression => IsInTargetTypedCastExpression(castExpression), - // a ? [1, 2, 3] : ... is target typed if either the other side is *not* a collection, - // or the entire ternary is target typed itself. - ConditionalExpressionSyntax conditionalExpression => IsInTargetTypedConditionalExpression(conditionalExpression, topExpression), - // Similar rules for switches. - SwitchExpressionArmSyntax switchExpressionArm => IsInTargetTypedSwitchExpressionArm(switchExpressionArm), - InitializerExpressionSyntax initializerExpression => IsInTargetTypedInitializerExpression(initializerExpression, topExpression), - CollectionElementSyntax collectionElement => IsInTargetTypedCollectionElement(collectionElement), - AssignmentExpressionSyntax assignmentExpression => IsInTargetTypedAssignmentExpression(assignmentExpression, topExpression), - BinaryExpressionSyntax binaryExpression => IsInTargetTypedBinaryExpression(binaryExpression, topExpression), - LambdaExpressionSyntax lambda => IsInTargetTypedLambdaExpression(lambda, topExpression), - ArgumentSyntax or AttributeArgumentSyntax => true, - ReturnStatementSyntax => true, - ArrowExpressionClauseSyntax => true, - _ => false, - }; - - bool HasType(ExpressionSyntax expression) - => semanticModel.GetTypeInfo(expression, cancellationToken).Type is not null and not IErrorTypeSymbol; - - static bool IsInTargetTypedEqualsValueClause(EqualsValueClauseSyntax equalsValue) - // If we're after an `x = ...` and it's not `var x`, this is target typed. - => equalsValue.Parent is not VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Type.IsVar: true } }; - - static bool IsInTargetTypedCastExpression(CastExpressionSyntax castExpression) - // (X[])[1, 2, 3] is target typed. `(X)[1, 2, 3]` is currently not (because it looks like indexing into an expr). - => castExpression.Type is not IdentifierNameSyntax; - - bool IsInTargetTypedConditionalExpression(ConditionalExpressionSyntax conditionalExpression, ExpressionSyntax expression) - { - if (conditionalExpression.WhenTrue == expression) - return HasType(conditionalExpression.WhenFalse) || IsInTargetTypedLocation(semanticModel, conditionalExpression, cancellationToken); - else if (conditionalExpression.WhenFalse == expression) - return HasType(conditionalExpression.WhenTrue) || IsInTargetTypedLocation(semanticModel, conditionalExpression, cancellationToken); - else - return false; - } - - bool IsInTargetTypedLambdaExpression(LambdaExpressionSyntax lambda, ExpressionSyntax expression) - => lambda.ExpressionBody == expression && IsInTargetTypedLocation(semanticModel, lambda, cancellationToken); - - bool IsInTargetTypedSwitchExpressionArm(SwitchExpressionArmSyntax switchExpressionArm) - { - var switchExpression = (SwitchExpressionSyntax)switchExpressionArm.GetRequiredParent(); - - // check if any other arm has a type that this would be target typed against. - foreach (var arm in switchExpression.Arms) - { - if (arm != switchExpressionArm && HasType(arm.Expression)) - return true; - } - - // All arms do not have a type, this is target typed if the switch itself is target typed. - return IsInTargetTypedLocation(semanticModel, switchExpression, cancellationToken); - } - - bool IsInTargetTypedCollectionElement(CollectionElementSyntax collectionElement) - { - // We do not currently target type spread expressions in a collection expression. - if (collectionElement is not ExpressionElementSyntax) - return false; - - // The element it target typed if the parent collection is itself target typed. - var collectionExpression = (CollectionExpressionSyntax)collectionElement.GetRequiredParent(); - return IsInTargetTypedLocation(semanticModel, collectionExpression, cancellationToken); - } - - bool IsInTargetTypedInitializerExpression(InitializerExpressionSyntax initializerExpression, ExpressionSyntax expression) - { - // new X[] { [1, 2, 3] }. Elements are target typed by array type. - if (initializerExpression.Parent is ArrayCreationExpressionSyntax) - return true; - - // new [] { [1, 2, 3], ... }. Elements are target typed if there's another element with real type. - if (initializerExpression.Parent is ImplicitArrayCreationExpressionSyntax) - { - foreach (var sibling in initializerExpression.Expressions) - { - if (sibling != expression && HasType(sibling)) - return true; - } - } - - // TODO: Handle these. - if (initializerExpression.Parent is StackAllocArrayCreationExpressionSyntax or ImplicitStackAllocArrayCreationExpressionSyntax) - return false; - - // T[] x = [1, 2, 3]; - if (initializerExpression.Parent is EqualsValueClauseSyntax) - return true; - - return false; - } - - bool IsInTargetTypedAssignmentExpression(AssignmentExpressionSyntax assignmentExpression, ExpressionSyntax expression) - { - return expression == assignmentExpression.Right && HasType(assignmentExpression.Left); - } - - bool IsInTargetTypedBinaryExpression(BinaryExpressionSyntax binaryExpression, ExpressionSyntax expression) - { - return binaryExpression.Kind() == SyntaxKind.CoalesceExpression && binaryExpression.Right == expression && HasType(binaryExpression.Left); - } - } - public static CollectionExpressionSyntax ConvertInitializerToCollectionExpression( InitializerExpressionSyntax initializer, bool wasOnSingleLine) { @@ -941,12 +849,14 @@ public static bool IsCollectionFactoryCreate( InvocationExpressionSyntax invocationExpression, [NotNullWhen(true)] out MemberAccessExpressionSyntax? memberAccess, out bool unwrapArgument, + out bool useSpread, CancellationToken cancellationToken) { const string CreateName = nameof(ImmutableArray.Create); const string CreateRangeName = nameof(ImmutableArray.CreateRange); unwrapArgument = false; + useSpread = false; memberAccess = null; // Looking for `XXX.Create(...)` @@ -988,16 +898,18 @@ public static bool IsCollectionFactoryCreate( // `Create(params T[])` (passing as individual elements, or an array with an initializer) // `Create(ReadOnlySpan)` (passing as a stack-alloc with an initializer) // `Create(IEnumerable)` (passing as something with an initializer. - if (!IsCompatibleSignatureAndArguments(createMethod.OriginalDefinition, out unwrapArgument)) + if (!IsCompatibleSignatureAndArguments(createMethod.OriginalDefinition, out unwrapArgument, out useSpread)) return false; return true; bool IsCompatibleSignatureAndArguments( IMethodSymbol originalCreateMethod, - out bool unwrapArgument) + out bool unwrapArgument, + out bool useSpread) { unwrapArgument = false; + useSpread = false; var arguments = invocationExpression.ArgumentList.Arguments; @@ -1016,30 +928,11 @@ bool IsCompatibleSignatureAndArguments( Name: nameof(IEnumerable), TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] } enumerableType - }] && enumerableType.OriginalDefinition.Equals(compilation.IEnumerableOfTType())) + }] && + enumerableType.OriginalDefinition.Equals(compilation.IEnumerableOfTType()) && + arguments.Count == 1) { - var argExpression = arguments[0].Expression; - if (argExpression - is ArrayCreationExpressionSyntax { Initializer: not null } - or ImplicitArrayCreationExpressionSyntax) - { - unwrapArgument = true; - return true; - } - - if (argExpression is ObjectCreationExpressionSyntax objectCreation) - { - // Can't have any arguments, as we cannot preserve them once we grab out all the elements. - if (objectCreation.ArgumentList != null && objectCreation.ArgumentList.Arguments.Count > 0) - return false; - - // If it's got an initializer, it has to be a collection initializer (or an empty object initializer); - if (objectCreation.Initializer.IsKind(SyntaxKind.ObjectCreationExpression) && objectCreation.Initializer.Expressions.Count > 0) - return false; - - unwrapArgument = true; - return true; - } + return IsArgumentCompatibleWithIEnumerableOfT(semanticModel, arguments[0], out unwrapArgument, out useSpread, cancellationToken); } } else if (originalCreateMethod.Name is CreateName) @@ -1076,13 +969,13 @@ bool IsCompatibleSignatureAndArguments( if (arguments.Count == 1 && compilation.SupportsRuntimeCapability(RuntimeCapability.InlineArrayTypes) && originalCreateMethod.Parameters is [ + { + Type: INamedTypeSymbol { - Type: INamedTypeSymbol - { - Name: nameof(Span) or nameof(ReadOnlySpan), - TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] - } spanType - }]) + Name: nameof(Span) or nameof(ReadOnlySpan), + TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] + } spanType + }]) { if (spanType.OriginalDefinition.Equals(compilation.SpanOfTType()) || spanType.OriginalDefinition.Equals(compilation.ReadOnlySpanOfTType())) @@ -1102,6 +995,78 @@ originalCreateMethod.Parameters is [ } } + public static bool IsArgumentCompatibleWithIEnumerableOfT( + SemanticModel semanticModel, ArgumentSyntax argument, out bool unwrapArgument, out bool useSpread, CancellationToken cancellationToken) + { + unwrapArgument = false; + useSpread = false; + + var argExpression = argument.Expression; + if (argExpression + is ArrayCreationExpressionSyntax { Initializer: not null } + or ImplicitArrayCreationExpressionSyntax) + { + unwrapArgument = true; + return true; + } + + if (argExpression is ObjectCreationExpressionSyntax objectCreation) + { + // Can't have any arguments, as we cannot preserve them once we grab out all the elements. + if (objectCreation.ArgumentList != null && objectCreation.ArgumentList.Arguments.Count > 0) + return false; + + // If it's got an initializer, it has to be a collection initializer (or an empty object initializer); + if (objectCreation.Initializer.IsKind(SyntaxKind.ObjectCreationExpression) && objectCreation.Initializer.Expressions.Count > 0) + return false; + + unwrapArgument = true; + return true; + } + + if (IsIterable(semanticModel, argExpression, cancellationToken)) + { + // Convert `ImmutableArray.Create(someEnumerable)` to `[.. someEnumerable]` + unwrapArgument = false; + useSpread = true; + return true; + } + + return false; + } + + public static bool IsIterable(SemanticModel semanticModel, ExpressionSyntax expression, CancellationToken cancellationToken) + { + var type = semanticModel.GetTypeInfo(expression, cancellationToken).Type; + if (type is null or IErrorTypeSymbol) + return false; + + if (BannedTypes.Contains(type.Name)) + return false; + + var compilation = semanticModel.Compilation; + return EqualsOrImplements(type, compilation.IEnumerableOfTType()) || + type.Equals(compilation.SpanOfTType()) || + type.Equals(compilation.ReadOnlySpanOfTType()); + } + + public static bool EqualsOrImplements(ITypeSymbol type, INamedTypeSymbol? interfaceType) + { + if (interfaceType != null) + { + if (type.OriginalDefinition.Equals(interfaceType)) + return true; + + foreach (var baseInterface in type.AllInterfaces) + { + if (interfaceType.Equals(baseInterface.OriginalDefinition)) + return true; + } + } + + return false; + } + public static bool IsCollectionEmptyAccess( SemanticModel semanticModel, ExpressionSyntax expression, @@ -1208,9 +1173,9 @@ static bool IsPossiblyDottedName(ExpressionSyntax name) } } - public static SeparatedSyntaxList GetArguments(InvocationExpressionSyntax invocationExpression, bool unwrapArgument) + public static SeparatedSyntaxList GetArguments(ArgumentListSyntax argumentList, bool unwrapArgument) { - var arguments = invocationExpression.ArgumentList.Arguments; + var arguments = argumentList.Arguments; // If we're not unwrapping a singular argument expression, then just pass back all the explicit argument // expressions the user wrote out. @@ -1239,4 +1204,20 @@ public static SeparatedSyntaxList GetArguments(InvocationExpress public static CollectionExpressionSyntax CreateReplacementCollectionExpressionForAnalysis(InitializerExpressionSyntax? initializer) => initializer is null ? s_emptyCollectionExpression : CollectionExpression([.. initializer.Expressions.Select(ExpressionElement)]); + + public static ImmutableDictionary GetDiagnosticProperties(bool unwrapArgument, bool useSpread, bool changesSemantics) + { + var properties = ImmutableDictionary.Empty; + + if (unwrapArgument) + properties = properties.Add(UnwrapArgument, ""); + + if (useSpread) + properties = properties.Add(UseSpread, ""); + + if (changesSemantics) + properties = properties.Add(UseCollectionInitializerHelpers.ChangesSemanticsName, ""); + + return properties; + } } diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs index 69b943c5b43f5..c0bd02c1d2d3c 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerDiagnosticAnalyzer.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.LanguageService; @@ -10,10 +13,13 @@ using Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.UseCollectionExpression; using Microsoft.CodeAnalysis.UseCollectionInitializer; namespace Microsoft.CodeAnalysis.CSharp.UseCollectionInitializer; +using static SyntaxFactory; + [DiagnosticAnalyzer(LanguageNames.CSharp)] internal sealed class CSharpUseCollectionInitializerDiagnosticAnalyzer : AbstractUseCollectionInitializerDiagnosticAnalyzer< @@ -40,13 +46,39 @@ protected override bool AreCollectionInitializersSupported(Compilation compilati protected override bool AreCollectionExpressionsSupported(Compilation compilation) => compilation.LanguageVersion().SupportsCollectionExpressions(); - protected override bool CanUseCollectionExpression(SemanticModel semanticModel, BaseObjectCreationExpressionSyntax objectCreationExpression, INamedTypeSymbol? expressionType, bool allowSemanticsChange, CancellationToken cancellationToken, out bool changesSemantics) + protected override bool CanUseCollectionExpression( + SemanticModel semanticModel, + BaseObjectCreationExpressionSyntax objectCreationExpression, + INamedTypeSymbol? expressionType, + ImmutableArray> preMatches, + bool allowSemanticsChange, + CancellationToken cancellationToken, + out bool changesSemantics) { // Synthesize the final collection expression we would replace this object-creation with. That will allow us to // determine if we end up calling the right overload in cases of overloaded methods. - var replacement = UseCollectionExpressionHelpers.CreateReplacementCollectionExpressionForAnalysis(objectCreationExpression.Initializer); + var replacement = CollectionExpression(SeparatedList( + GetMatchElements(preMatches).Concat(GetInitializerElements(objectCreationExpression.Initializer)))); return UseCollectionExpressionHelpers.CanReplaceWithCollectionExpression( semanticModel, objectCreationExpression, replacement, expressionType, isSingletonInstance: false, allowSemanticsChange, skipVerificationForReplacedNode: true, cancellationToken, out changesSemantics); + + static IEnumerable GetMatchElements(ImmutableArray> preMatches) + { + foreach (var match in preMatches) + { + if (match.Node is ExpressionSyntax expression) + yield return match.UseSpread ? SpreadElement(expression) : ExpressionElement(expression); + } + } + + static IEnumerable GetInitializerElements(InitializerExpressionSyntax? initializer) + { + if (initializer != null) + { + foreach (var expression in initializer.Expressions) + yield return ExpressionElement(expression); + } + } } } diff --git a/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpUseNotPatternDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpUseNotPatternDiagnosticAnalyzer.cs index da2601fd3a508..9638b15efc86f 100644 --- a/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpUseNotPatternDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpUseNotPatternDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; @@ -98,7 +97,7 @@ private void SyntaxNodeAction( isKeyword.GetLocation(), styleOption.Notification, context.Options, - ImmutableArray.Create(node.GetLocation()), + [node.GetLocation()], properties: null)); } } diff --git a/src/Analyzers/CSharp/Analyzers/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs index 6535e0e24d206..cf6e90058982d 100644 --- a/src/Analyzers/CSharp/Analyzers/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseSimpleUsingStatement/UseSimpleUsingStatementDiagnosticAnalyzer.cs @@ -2,16 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.LanguageService; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Utilities; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseSimpleUsingStatement; @@ -48,17 +52,14 @@ namespace Microsoft.CodeAnalysis.CSharp.UseSimpleUsingStatement; /// semantics will not change. /// [DiagnosticAnalyzer(LanguageNames.CSharp)] -internal class UseSimpleUsingStatementDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiagnosticAnalyzer +internal sealed class UseSimpleUsingStatementDiagnosticAnalyzer() + : AbstractBuiltInCodeStyleDiagnosticAnalyzer( + IDEDiagnosticIds.UseSimpleUsingStatementDiagnosticId, + EnforceOnBuildValues.UseSimpleUsingStatement, + CSharpCodeStyleOptions.PreferSimpleUsingStatement, + new LocalizableResourceString(nameof(CSharpAnalyzersResources.Use_simple_using_statement), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)), + new LocalizableResourceString(nameof(CSharpAnalyzersResources.using_statement_can_be_simplified), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) { - public UseSimpleUsingStatementDiagnosticAnalyzer() - : base(IDEDiagnosticIds.UseSimpleUsingStatementDiagnosticId, - EnforceOnBuildValues.UseSimpleUsingStatement, - CSharpCodeStyleOptions.PreferSimpleUsingStatement, - new LocalizableResourceString(nameof(CSharpAnalyzersResources.Use_simple_using_statement), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)), - new LocalizableResourceString(nameof(CSharpAnalyzersResources.using_statement_can_be_simplified), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) - { - } - public override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; @@ -83,12 +84,14 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) var outermostUsing = (UsingStatementSyntax)context.Node; var semanticModel = context.SemanticModel; - if (outermostUsing.Parent is not BlockSyntax parentBlock) - { - // Don't offer on a using statement that is parented by another using statement. We'll just offer on the - // topmost using statement. + var parentBlockLike = outermostUsing.Parent; + if (parentBlockLike is GlobalStatementSyntax) + parentBlockLike = parentBlockLike.Parent; + + // Don't offer on a using statement that is parented by another using statement. We'll just offer on the + // topmost using statement. + if (parentBlockLike is not BlockSyntax and not CompilationUnitSyntax) return; - } var innermostUsing = outermostUsing; @@ -102,7 +105,7 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) } // Verify that changing this using-statement into a using-declaration will not change semantics. - if (!PreservesSemantics(semanticModel, parentBlock, outermostUsing, innermostUsing, cancellationToken)) + if (!PreservesSemantics(semanticModel, parentBlockLike, outermostUsing, innermostUsing, cancellationToken)) return; // Converting a using-statement to a using-variable-declaration will cause the using's variables to now be @@ -110,7 +113,7 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) // block. These may then collide with other variables in the block, causing an error. Check for that and // bail if this happens. if (CausesVariableCollision( - context.SemanticModel, parentBlock, + context.SemanticModel, parentBlockLike, outermostUsing, innermostUsing, cancellationToken)) { return; @@ -122,44 +125,61 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) outermostUsing.UsingKeyword.GetLocation(), option.Notification, context.Options, - additionalLocations: ImmutableArray.Create(outermostUsing.GetLocation()), + additionalLocations: [outermostUsing.GetLocation()], properties: null)); } private static bool CausesVariableCollision( - SemanticModel semanticModel, BlockSyntax parentBlock, - UsingStatementSyntax outermostUsing, UsingStatementSyntax innermostUsing, + SemanticModel semanticModel, + SyntaxNode parentBlockLike, + UsingStatementSyntax outermostUsing, + UsingStatementSyntax innermostUsing, CancellationToken cancellationToken) { - var symbolNameToExistingSymbol = semanticModel.GetExistingSymbols(parentBlock, cancellationToken).ToLookup(s => s.Name); + using var _ = PooledDictionary>.GetInstance(out var symbolNameToExistingSymbol); - for (var current = outermostUsing; current != null; current = current.Statement as UsingStatementSyntax) + try { - // Check if the using statement itself contains variables that will collide with other variables in the - // block. - var usingOperation = (IUsingOperation)semanticModel.GetRequiredOperation(current, cancellationToken); - if (DeclaredLocalCausesCollision(symbolNameToExistingSymbol, usingOperation.Locals)) - return true; + foreach (var statement in CSharpBlockFacts.Instance.GetExecutableBlockStatements(parentBlockLike)) + { + foreach (var symbol in semanticModel.GetExistingSymbols(statement, cancellationToken)) + symbolNameToExistingSymbol.MultiAdd(symbol.Name, symbol); + } + + for (var current = outermostUsing; current != null; current = current.Statement as UsingStatementSyntax) + { + // Check if the using statement itself contains variables that will collide with other variables in the + // block. + var usingOperation = (IUsingOperation)semanticModel.GetRequiredOperation(current, cancellationToken); + if (DeclaredLocalCausesCollision(symbolNameToExistingSymbol, usingOperation.Locals)) + return true; + } + + var innerUsingOperation = (IUsingOperation)semanticModel.GetRequiredOperation(innermostUsing, cancellationToken); + if (innerUsingOperation.Body is IBlockOperation innerUsingBlock) + return DeclaredLocalCausesCollision(symbolNameToExistingSymbol, innerUsingBlock.Locals); + + return false; + } + finally + { + symbolNameToExistingSymbol.FreeValues(); } - - var innerUsingOperation = (IUsingOperation)semanticModel.GetRequiredOperation(innermostUsing, cancellationToken); - if (innerUsingOperation.Body is IBlockOperation innerUsingBlock) - return DeclaredLocalCausesCollision(symbolNameToExistingSymbol, innerUsingBlock.Locals); - - return false; } - private static bool DeclaredLocalCausesCollision(ILookup symbolNameToExistingSymbol, ImmutableArray locals) - => locals.Any(static (local, symbolNameToExistingSymbol) => symbolNameToExistingSymbol[local.Name].Any(otherLocal => !local.Equals(otherLocal)), symbolNameToExistingSymbol); + private static bool DeclaredLocalCausesCollision(Dictionary> symbolNameToExistingSymbol, ImmutableArray locals) + => locals.Any(static (local, symbolNameToExistingSymbol) => + symbolNameToExistingSymbol.TryGetValue(local.Name, out var symbols) && + symbols.Any(otherLocal => !local.Equals(otherLocal)), symbolNameToExistingSymbol); private static bool PreservesSemantics( SemanticModel semanticModel, - BlockSyntax parentBlock, + SyntaxNode parentBlockLike, UsingStatementSyntax outermostUsing, UsingStatementSyntax innermostUsing, CancellationToken cancellationToken) { - var statements = parentBlock.Statements; + var statements = (IReadOnlyList)CSharpBlockFacts.Instance.GetExecutableBlockStatements(parentBlockLike); var index = statements.IndexOf(outermostUsing); return UsingValueDoesNotLeakToFollowingStatements(semanticModel, statements, index, cancellationToken) && @@ -167,7 +187,7 @@ private static bool PreservesSemantics( } private static bool UsingStatementDoesNotInvolveJumps( - SyntaxList parentStatements, int index, UsingStatementSyntax innermostUsing) + IReadOnlyList parentStatements, int index, UsingStatementSyntax innermostUsing) { // Jumps are not allowed to cross a using declaration in the forward direction, and can't go back unless // there is a curly brace between the using and the label. @@ -204,7 +224,7 @@ private static bool IsGotoOrLabeledStatement(StatementSyntax priorStatement) private static bool UsingValueDoesNotLeakToFollowingStatements( SemanticModel semanticModel, - SyntaxList statements, + IReadOnlyList statements, int index, CancellationToken cancellationToken) { diff --git a/src/Analyzers/CSharp/Analyzers/UseUnboundGenericTypeInNameOf/CSharpUseUnboundGenericTypeInNameOfDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseUnboundGenericTypeInNameOf/CSharpUseUnboundGenericTypeInNameOfDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..09891f4af4519 --- /dev/null +++ b/src/Analyzers/CSharp/Analyzers/UseUnboundGenericTypeInNameOf/CSharpUseUnboundGenericTypeInNameOfDiagnosticAnalyzer.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Linq; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.CSharp.UseUnboundGenericTypeInNameOf; + +/// +/// Looks for code of the form: +/// +/// +/// nameof(List<...>) +/// +/// +/// and converts it to: +/// +/// +/// nameof(List<>) +/// +/// +/// +[DiagnosticAnalyzer(LanguageNames.CSharp)] +internal sealed class CSharpUseUnboundGenericTypeInNameOfDiagnosticAnalyzer() + : AbstractBuiltInCodeStyleDiagnosticAnalyzer( + IDEDiagnosticIds.UseUnboundGenericTypeInNameOfDiagnosticId, + EnforceOnBuildValues.UseUnboundGenericTypeInNameOf, + CSharpCodeStyleOptions.PreferUnboundGenericTypeInNameOf, + new LocalizableResourceString( + nameof(CSharpAnalyzersResources.Use_unbound_generic_type), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) +{ + public override DiagnosticAnalyzerCategory GetAnalyzerCategory() + => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; + + protected override void InitializeWorker(AnalysisContext context) + { + context.RegisterCompilationStartAction(context => + { + // Tuples are only available in C# 14 and above. + var compilation = context.Compilation; + if (!compilation.LanguageVersion().IsCSharp14OrAbove()) + return; + + context.RegisterSyntaxNodeAction( + AnalyzeInvocationExpression, + SyntaxKind.InvocationExpression); + }); + } + + private void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext syntaxContext) + { + var cancellationToken = syntaxContext.CancellationToken; + var styleOption = syntaxContext.GetCSharpAnalyzerOptions().PreferUnboundGenericTypeInNameOf; + if (!styleOption.Value || ShouldSkipAnalysis(syntaxContext, styleOption.Notification)) + return; + + var invocation = (InvocationExpressionSyntax)syntaxContext.Node; + if (!invocation.IsNameOfInvocation()) + return; + + foreach (var typeArgumentList in invocation.DescendantNodesAndSelf().OfType()) + { + foreach (var argument in typeArgumentList.Arguments) + { + if (argument.Kind() != SyntaxKind.OmittedTypeArgument) + { + syntaxContext.ReportDiagnostic(DiagnosticHelper.CreateWithLocationTags( + Descriptor, + invocation.GetFirstToken().GetLocation(), + styleOption.Notification, + syntaxContext.Options, + [invocation.GetLocation()], + additionalUnnecessaryLocations: [invocation.SyntaxTree.GetLocation( + TextSpan.FromBounds(typeArgumentList.LessThanToken.Span.End, typeArgumentList.GreaterThanToken.Span.Start))])); + + return; + } + } + } + } +} diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf index 62f46796ecfab..fd92a148a5131 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf @@ -422,6 +422,11 @@ Prohození hodnot pomocí řazené kolekce členů + + Use unbound generic type + Use unbound generic type + + Using directives must be placed inside of a namespace declaration Direktivy using se musí umístit do deklarace namespace. diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf index accb3909571de..62aefa1dfa65f 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf @@ -422,6 +422,11 @@ Tupel zum Tauschen von Werten verwenden + + Use unbound generic type + Use unbound generic type + + Using directives must be placed inside of a namespace declaration Using-Anweisungen müssen in einer namespace-Deklaration platziert werden. diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf index d7855c6a310b3..1c664a907efdc 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf @@ -422,6 +422,11 @@ Utilizar tupla para intercambiar valores + + Use unbound generic type + Use unbound generic type + + Using directives must be placed inside of a namespace declaration Las directivas using deben colocarse dentro de una declaración de namespace diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf index e8f1a51941216..3104c437e6a0d 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf @@ -422,6 +422,11 @@ Utilisez le tuple pour échanger des valeurs + + Use unbound generic type + Use unbound generic type + + Using directives must be placed inside of a namespace declaration Les directives using doivent être placées dans une déclaration namespace diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf index 4568f9346ec71..c140510bbdf48 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf @@ -422,6 +422,11 @@ Usa la tupla per scambiare i valori + + Use unbound generic type + Use unbound generic type + + Using directives must be placed inside of a namespace declaration Le direttive using devono essere inserite all'interno di una dichiarazione di namespace diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf index 40b6033cee3da..4b66e8433b16d 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf @@ -422,6 +422,11 @@ タプルを使用して値をスワップする + + Use unbound generic type + Use unbound generic type + + Using directives must be placed inside of a namespace declaration using ディレクティブを namespace 宣言の中に配置する必要があります diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf index a450fc7a71c47..b31a57f0641d0 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf @@ -422,6 +422,11 @@ 튜플을 사용하여 값 바꾸기 + + Use unbound generic type + Use unbound generic type + + Using directives must be placed inside of a namespace declaration Using 지시문은 namespace 선언 내부에 배치되어야 합니다. diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf index f4e2d9f1ce44e..72bec10276155 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf @@ -422,6 +422,11 @@ Użyj krotki do zamiany wartości + + Use unbound generic type + Use unbound generic type + + Using directives must be placed inside of a namespace declaration Dyrektywy using muszą znajdować się wewnątrz deklaracji namespace diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf index d23edd5e0edf4..0b0f9af906709 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf @@ -422,6 +422,11 @@ Usar a tupla para trocar valores + + Use unbound generic type + Use unbound generic type + + Using directives must be placed inside of a namespace declaration As diretivas using precisam ser colocadas dentro de uma declaração de namespace diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf index ecb6219478e28..2498b23b3f3be 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf @@ -422,6 +422,11 @@ Использовать кортеж для переключения значений + + Use unbound generic type + Use unbound generic type + + Using directives must be placed inside of a namespace declaration Директивы using должны находиться внутри объявления namespace diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf index 1b93fd9b6ccad..973bf1a1f2495 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf @@ -422,6 +422,11 @@ Değerleri değiştirmek için demeti kullanın + + Use unbound generic type + Use unbound generic type + + Using directives must be placed inside of a namespace declaration Using yönergelerinin bir namespace bildiriminin içine yerleştirilmesi gerekir diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf index ea18e9dbd42f5..e3251991e3d76 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf @@ -422,6 +422,11 @@ 使用元组交换值 + + Use unbound generic type + Use unbound generic type + + Using directives must be placed inside of a namespace declaration using 指令必须放在 namespace 声明的内部 diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf index ae7a62524cb9c..81083e318ef16 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf @@ -422,6 +422,11 @@ 使用元組交換值 + + Use unbound generic type + Use unbound generic type + + Using directives must be placed inside of a namespace declaration using 指示詞必須放在 namespace 宣告內 diff --git a/src/Analyzers/CSharp/CodeFixes/AddInheritdoc/AddInheritdocCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/AddInheritdoc/AddInheritdocCodeFixProvider.cs index eab69a511fc47..465f33a6f4f84 100644 --- a/src/Analyzers/CSharp/CodeFixes/AddInheritdoc/AddInheritdocCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/AddInheritdoc/AddInheritdocCodeFixProvider.cs @@ -8,18 +8,18 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Text; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using Microsoft.CodeAnalysis.CSharp.Extensions; namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.AddInheritdoc; using static CSharpSyntaxTokens; +using static SyntaxFactory; [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddInheritdoc), Shared] [method: ImportingConstructor] @@ -37,33 +37,21 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; var cancellationToken = context.CancellationToken; - var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - if (root is null) - { - return; - } + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - SemanticModel? semanticModel = null; + var semanticModel = await context.Document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); foreach (var diagnostic in context.Diagnostics) { var node = root.FindNode(diagnostic.Location.SourceSpan); if (node.Kind() is not SyntaxKind.MethodDeclaration and not SyntaxKind.PropertyDeclaration and not SyntaxKind.VariableDeclarator) - { continue; - } - if (node.IsKind(SyntaxKind.VariableDeclarator) && node.Parent?.Parent?.IsKind(SyntaxKind.EventFieldDeclaration) == false) - { + if (node.IsKind(SyntaxKind.VariableDeclarator) && node is not { Parent.Parent: EventFieldDeclarationSyntax }) continue; - } - - semanticModel ??= await context.Document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var symbol = semanticModel.GetDeclaredSymbol(node, cancellationToken); if (symbol is null) - { continue; - } if (symbol.Kind is SymbolKind.Method or SymbolKind.Property or SymbolKind.Event) { @@ -78,21 +66,15 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) protected override async Task FixAllAsync(Document document, ImmutableArray diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) { - string? newLine = null; - SourceText? sourceText = null; + var options = await document.GetLineFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); + var newLine = options.NewLine; + var sourceText = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + foreach (var diagnostic in diagnostics) { var node = editor.OriginalRoot.FindNode(diagnostic.Location.SourceSpan); - if (node.IsKind(SyntaxKind.VariableDeclarator) && !(node = node.Parent?.Parent).IsKind(SyntaxKind.EventFieldDeclaration)) - { - continue; - } - - if (newLine == null) - { - var options = await document.GetLineFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); - newLine = options.NewLine; - } + if (node is VariableDeclaratorSyntax { Parent.Parent: EventFieldDeclarationSyntax eventField }) + node = eventField; // We can safely assume, that there is no leading doc comment, because that is what CS1591 is telling us. // So we create a new /// comment. @@ -112,13 +94,21 @@ protected override async Task FixAllAsync(Document document, ImmutableArray + @@ -137,7 +138,6 @@ - @@ -176,6 +176,7 @@ + diff --git a/src/Analyzers/CSharp/CodeFixes/ImplementInterface/CSharpImplementInterfaceCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/ImplementInterface/CSharpImplementInterfaceCodeFixProvider.cs index 87ff9b07f4752..0e57a94e02e1f 100644 --- a/src/Analyzers/CSharp/CodeFixes/ImplementInterface/CSharpImplementInterfaceCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/ImplementInterface/CSharpImplementInterfaceCodeFixProvider.cs @@ -15,7 +15,8 @@ namespace Microsoft.CodeAnalysis.CSharp.ImplementInterface; [ExtensionOrder(After = PredefinedCodeFixProviderNames.ImplementAbstractClass)] [method: ImportingConstructor] [method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] -internal sealed class CSharpImplementInterfaceCodeFixProvider() : AbstractImplementInterfaceCodeFixProvider +internal sealed class CSharpImplementInterfaceCodeFixProvider() + : AbstractImplementInterfaceCodeFixProvider { private const string CS0535 = nameof(CS0535); // 'Program' does not implement interface member 'System.Collections.IEnumerable.GetEnumerator()' private const string CS0737 = nameof(CS0737); // 'Class' does not implement interface member 'IInterface.M()'. 'Class.M()' cannot implement an interface member because it is not public. diff --git a/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider.cs index d219c3bd03a0b..cb003a2520721 100644 --- a/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider.cs @@ -3,8 +3,10 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; @@ -12,8 +14,10 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Utilities; namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryLambdaExpression; @@ -39,7 +43,9 @@ protected override Task FixAllAsync( { foreach (var diagnostic in diagnostics) { - var anonymousFunction = diagnostic.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken); + var anonymousFunction = diagnostic.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken) as AnonymousFunctionExpressionSyntax; + if (anonymousFunction is null) + continue; editor.ReplaceNode(anonymousFunction, (current, generator) => @@ -52,8 +58,32 @@ protected override Task FixAllAsync( return current; }); + + // If the inner invocation has important trivia on it, move it to the container of the anonymous function. + if (TryGetAnonymousFunctionInvocation(anonymousFunction, out var invocation, out _) && + invocation.GetLeadingTrivia().Any(t => t.IsSingleOrMultiLineComment())) + { + var containingStatement = anonymousFunction.AncestorsAndSelf().OfType().FirstOrDefault(); + if (containingStatement != null) + { + editor.ReplaceNode(containingStatement, + (current, generator) => current + .WithPrependedLeadingTrivia(TakeComments(invocation.GetLeadingTrivia())) + .WithAdditionalAnnotations(Formatter.Annotation)); + } + } } return Task.CompletedTask; } + + private static IEnumerable TakeComments(SyntaxTriviaList triviaList) + { + var lastComment = triviaList.Last(t => t.IsSingleOrMultiLineComment()); + var lastIndex = triviaList.IndexOf(lastComment) + 1; + if (lastIndex < triviaList.Count && triviaList[lastIndex].IsEndOfLine()) + lastIndex++; + + return triviaList.Take(lastIndex); + } } diff --git a/src/Analyzers/CSharp/CodeFixes/SimplifyLinqExpression/CSharpSimplifyLinqExpressionCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/SimplifyLinqExpression/CSharpSimplifyLinqExpressionCodeFixProvider.cs deleted file mode 100644 index ea9061d1df6eb..0000000000000 --- a/src/Analyzers/CSharp/CodeFixes/SimplifyLinqExpression/CSharpSimplifyLinqExpressionCodeFixProvider.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Composition; -using System.Diagnostics.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.LanguageService; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.SimplifyLinqExpression; - -namespace Microsoft.CodeAnalysis.CSharp.SimplifyLinqExpression; - -[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.SimplifyLinqExpression), Shared] -[method: ImportingConstructor] -[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] -internal sealed class CSharpSimplifyLinqExpressionCodeFixProvider() : AbstractSimplifyLinqExpressionCodeFixProvider -{ - protected override ISyntaxFacts SyntaxFacts => CSharpSyntaxFacts.Instance; -} diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForCreateCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForCreateCodeFixProvider.cs index ee052d56fe70e..d29cecd68c36e 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForCreateCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForCreateCodeFixProvider.cs @@ -21,6 +21,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; using static CSharpCollectionExpressionRewriter; using static SyntaxFactory; +using static UseCollectionExpressionHelpers; [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseCollectionExpressionForCreate), Shared] [method: ImportingConstructor] @@ -39,7 +40,8 @@ protected override async Task FixAsync( ImmutableDictionary properties, CancellationToken cancellationToken) { - var unwrapArgument = properties.ContainsKey(CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.UnwrapArgument); + var unwrapArgument = properties.ContainsKey(UnwrapArgument); + var useSpread = properties.ContainsKey(UseSpread); // We want to replace `XXX.Create(...)` with the new collection expression. To do this, we go through the // following steps. First, we replace `XXX.Create(a, b, c)` with `new(a, b, c)` (a dummy object creation @@ -51,7 +53,7 @@ protected override async Task FixAsync( var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); // Get the expressions that we're going to fill the new collection expression with. - var arguments = UseCollectionExpressionHelpers.GetArguments(invocationExpression, unwrapArgument); + var arguments = GetArguments(invocationExpression.ArgumentList, unwrapArgument); var dummyObjectAnnotation = new SyntaxAnnotation(); var dummyObjectCreation = ImplicitObjectCreationExpression(ArgumentList(arguments), initializer: null) @@ -62,7 +64,7 @@ protected override async Task FixAsync( semanticDocument.Root.ReplaceNode(invocationExpression, dummyObjectCreation), cancellationToken).ConfigureAwait(false); dummyObjectCreation = (ImplicitObjectCreationExpressionSyntax)newSemanticDocument.Root.GetAnnotatedNodes(dummyObjectAnnotation).Single(); var expressions = dummyObjectCreation.ArgumentList.Arguments.Select(a => a.Expression); - var matches = expressions.SelectAsArray(static e => new CollectionMatch(e, UseSpread: false)); + var matches = expressions.SelectAsArray(e => new CollectionMatch(e, useSpread)); var collectionExpression = await CreateCollectionExpressionAsync( newSemanticDocument.Document, diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForNewCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForNewCodeFixProvider.cs new file mode 100644 index 0000000000000..4111e3e2db128 --- /dev/null +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForNewCodeFixProvider.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.UseCollectionExpression; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; + +using static CSharpCollectionExpressionRewriter; +using static SyntaxFactory; +using static UseCollectionExpressionHelpers; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseCollectionExpressionForNew), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed partial class CSharpUseCollectionExpressionForNewCodeFixProvider() + : AbstractUseCollectionExpressionCodeFixProvider( + CSharpCodeFixesResources.Use_collection_expression, + IDEDiagnosticIds.UseCollectionExpressionForNewDiagnosticId) +{ + public override ImmutableArray FixableDiagnosticIds { get; } = [IDEDiagnosticIds.UseCollectionExpressionForNewDiagnosticId]; + + protected override async Task FixAsync( + Document document, + SyntaxEditor editor, + BaseObjectCreationExpressionSyntax objectCreationExpression, + ImmutableDictionary properties, + CancellationToken cancellationToken) + { + var unwrapArgument = properties.ContainsKey(UnwrapArgument); + var useSpread = properties.ContainsKey(UseSpread); + + // We want to replace `new ...(...)` with the new collection expression. To do this, we go through the + // following steps. First, we replace `new XXX(a, b, c)` with `new(a, b, c)` (a dummy object creation + // expression). We then call into our helper which replaces expressions with collection expressions. The reason + // for the dummy object creation expression is that it serves as an actual node the rewriting code can attach an + // initializer to, by which it can figure out appropriate wrapping and indentation for the collection expression + // elements. + + var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); + + // Get the expressions that we're going to fill the new collection expression with. + var arguments = GetArguments(objectCreationExpression.ArgumentList!, unwrapArgument); + + var dummyObjectAnnotation = new SyntaxAnnotation(); + var dummyObjectCreation = ImplicitObjectCreationExpression(ArgumentList(arguments), initializer: null) + .WithTriviaFrom(objectCreationExpression) + .WithAdditionalAnnotations(dummyObjectAnnotation); + + var newSemanticDocument = await semanticDocument.WithSyntaxRootAsync( + semanticDocument.Root.ReplaceNode(objectCreationExpression, dummyObjectCreation), cancellationToken).ConfigureAwait(false); + dummyObjectCreation = (ImplicitObjectCreationExpressionSyntax)newSemanticDocument.Root.GetAnnotatedNodes(dummyObjectAnnotation).Single(); + var expressions = dummyObjectCreation.ArgumentList.Arguments.Select(a => a.Expression); + var matches = expressions.SelectAsArray(e => new CollectionMatch(e, useSpread)); + + var collectionExpression = await CreateCollectionExpressionAsync( + newSemanticDocument.Document, + dummyObjectCreation, + preMatches: [], + matches, + static o => o.Initializer, + static (o, i) => o.WithInitializer(i), + cancellationToken).ConfigureAwait(false); + + editor.ReplaceNode(objectCreationExpression, collectionExpression); + } +} diff --git a/src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs index f2feec8761fe0..2baa187fe874e 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs @@ -2,13 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Linq; +using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; @@ -21,6 +20,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseSimpleUsingStatement; @@ -46,14 +46,15 @@ protected override Task FixAllAsync( Document document, ImmutableArray diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) { - var topmostUsingStatements = diagnostics.Select(d => (UsingStatementSyntax)d.AdditionalLocations[0].FindNode(cancellationToken)).ToSet(); - var blocks = topmostUsingStatements.Select(u => (BlockSyntax)u.Parent); + var topmostUsingStatements = diagnostics.Select( + d => (UsingStatementSyntax)d.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken)).ToSet(); + var blockLikes = topmostUsingStatements.Select(u => u.Parent is GlobalStatementSyntax ? u.Parent.GetRequiredParent() : u.GetRequiredParent()).ToSet(); // Process blocks in reverse order so we rewrite from inside-to-outside with nested // usings. var root = editor.OriginalRoot; var updatedRoot = root.ReplaceNodes( - blocks.OrderByDescending(b => b.SpanStart), + blockLikes.OrderByDescending(b => b.SpanStart), (original, current) => RewriteBlock(original, current, topmostUsingStatements)); editor.ReplaceNode(root, updatedRoot); @@ -61,26 +62,57 @@ protected override Task FixAllAsync( return Task.CompletedTask; } - private static BlockSyntax RewriteBlock( - BlockSyntax originalBlock, BlockSyntax currentBlock, + private static SyntaxNode RewriteBlock( + SyntaxNode originalBlockLike, + SyntaxNode currentBlockLike, ISet topmostUsingStatements) { - if (originalBlock.Statements.Count == currentBlock.Statements.Count) + var originalBlockStatements = (IReadOnlyList)CSharpBlockFacts.Instance.GetExecutableBlockStatements(originalBlockLike); + var currentBlockStatements = (IReadOnlyList)CSharpBlockFacts.Instance.GetExecutableBlockStatements(currentBlockLike); + + if (originalBlockStatements.Count == currentBlockStatements.Count) { - var statementToUpdateIndex = originalBlock.Statements.IndexOf(s => topmostUsingStatements.Contains(s)); - var statementToUpdate = currentBlock.Statements[statementToUpdateIndex]; + var statementToUpdateIndex = IndexOf(originalBlockStatements, s => topmostUsingStatements.Contains(s)); + var statementToUpdate = currentBlockStatements[statementToUpdateIndex]; if (statementToUpdate is UsingStatementSyntax usingStatement && usingStatement.Declaration != null) { - var updatedStatements = currentBlock.Statements.ReplaceRange( - statementToUpdate, - Expand(usingStatement)); - return currentBlock.WithStatements(updatedStatements); + var expandedUsing = Expand(usingStatement); + + return WithStatements(currentBlockLike, usingStatement, expandedUsing); } } - return currentBlock; + return currentBlockLike; + } + + public static int IndexOf(IReadOnlyList list, Func predicate) + { + for (var i = 0; i < list.Count; i++) + { + if (predicate(list[i])) + return i; + } + + return -1; + } + + private static SyntaxNode WithStatements( + SyntaxNode currentBlockLike, + UsingStatementSyntax usingStatement, + ImmutableArray expandedUsingStatements) + { + return currentBlockLike switch + { + BlockSyntax currentBlock => currentBlock.WithStatements( + currentBlock.Statements.ReplaceRange(usingStatement, expandedUsingStatements)), + + CompilationUnitSyntax compilationUnit => compilationUnit.WithMembers( + compilationUnit.Members.ReplaceRange((GlobalStatementSyntax)usingStatement.GetRequiredParent(), expandedUsingStatements.Select(GlobalStatement))), + + _ => throw ExceptionUtilities.UnexpectedValue(currentBlockLike), + }; } private static ImmutableArray Expand(UsingStatementSyntax usingStatement) @@ -163,15 +195,13 @@ private static SyntaxTriviaList Expand(ArrayBuilder result, Usi } return default; - } - private static LocalDeclarationStatementSyntax Convert(UsingStatementSyntax usingStatement) - { - return LocalDeclarationStatement( - usingStatement.AwaitKeyword, - usingStatement.UsingKeyword.WithAppendedTrailingTrivia(ElasticMarker), - modifiers: default, - usingStatement.Declaration, - SemicolonToken).WithTrailingTrivia(usingStatement.CloseParenToken.TrailingTrivia); + static LocalDeclarationStatementSyntax Convert(UsingStatementSyntax usingStatement) + => LocalDeclarationStatement( + usingStatement.AwaitKeyword, + usingStatement.UsingKeyword.WithAppendedTrailingTrivia(ElasticMarker), + modifiers: default, + usingStatement.Declaration!, + SemicolonToken).WithTrailingTrivia(usingStatement.CloseParenToken.TrailingTrivia); } } diff --git a/src/Analyzers/CSharp/CodeFixes/UseUnboundGenericTypeInNameOf/CSharpUseUnboundGenericTypeInNameOfCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseUnboundGenericTypeInNameOf/CSharpUseUnboundGenericTypeInNameOfCodeFixProvider.cs new file mode 100644 index 0000000000000..657f4593e77d7 --- /dev/null +++ b/src/Analyzers/CSharp/CodeFixes/UseUnboundGenericTypeInNameOf/CSharpUseUnboundGenericTypeInNameOfCodeFixProvider.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.CSharp.UseUnboundGenericTypeInNameOf; + +using static SyntaxFactory; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseUnboundGenericTypeInNameOf), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed partial class CSharpUseUnboundGenericTypeInNameOfCodeFixProvider() : SyntaxEditorBasedCodeFixProvider +{ + private static readonly SyntaxNodeOrToken s_omittedArgument = (SyntaxNodeOrToken)OmittedTypeArgument(); + + public override ImmutableArray FixableDiagnosticIds { get; } + = [IDEDiagnosticIds.UseUnboundGenericTypeInNameOfDiagnosticId]; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + RegisterCodeFix(context, CSharpAnalyzersResources.Use_unbound_generic_type, nameof(CSharpAnalyzersResources.Use_unbound_generic_type)); + return Task.CompletedTask; + } + + protected override Task FixAllAsync( + Document document, ImmutableArray diagnostics, + SyntaxEditor editor, CancellationToken cancellationToken) + { + foreach (var diagnostic in diagnostics) + FixOne(editor, diagnostic, cancellationToken); + + return Task.CompletedTask; + } + + private static void FixOne( + SyntaxEditor editor, Diagnostic diagnostic, CancellationToken cancellationToken) + { + var nameofInvocation = (InvocationExpressionSyntax)diagnostic.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken); + if (!nameofInvocation.IsNameOfInvocation()) + return; + + foreach (var typeArgumentList in nameofInvocation.DescendantNodes().OfType().OrderByDescending(t => t.SpanStart)) + { + if (typeArgumentList.Arguments.Any(a => a.Kind() != SyntaxKind.OmittedTypeArgument)) + { + var list = NodeOrTokenList(typeArgumentList.Arguments.GetWithSeparators().Select( + t => t.IsToken ? t.AsToken().WithoutTrivia() : s_omittedArgument)); + editor.ReplaceNode(typeArgumentList, typeArgumentList.WithArguments(SeparatedList(list))); + } + } + } +} diff --git a/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.cs b/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.cs index 28a7bdb9bd464..8f432ae3a5330 100644 --- a/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.cs +++ b/src/Analyzers/CSharp/Tests/AddAccessibilityModifiers/AddAccessibilityModifiersTests.cs @@ -622,7 +622,6 @@ internal class C : I var test = new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp12, ReferenceAssemblies = Testing.ReferenceAssemblies.Net.Net60 }; @@ -645,7 +644,6 @@ public async Task TestFileDeclaration(string declarationKind) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp12, }.RunAsync(); } @@ -658,7 +656,6 @@ public async Task TestFileDelegate() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp12, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/AddInheritdoc/AddInheritdocTests.cs b/src/Analyzers/CSharp/Tests/AddInheritdoc/AddInheritdocTests.cs index 78f5dc5b9a16a..029b1309b31b1 100644 --- a/src/Analyzers/CSharp/Tests/AddInheritdoc/AddInheritdocTests.cs +++ b/src/Analyzers/CSharp/Tests/AddInheritdoc/AddInheritdocTests.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.AddInheritdoc; @@ -16,24 +17,21 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.AddInheritd AddInheritdocCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsAddInheritdoc)] -public class AddInheritdocTests +public sealed class AddInheritdocTests { - private static async Task TestAsync(string initialMarkup, string expectedMarkup) - { - var test = new VerifyCS.Test + private static Task TestAsync(string initialMarkup, string expectedMarkup) + => new VerifyCS.Test { TestCode = initialMarkup, FixedCode = expectedMarkup, CodeActionValidationMode = CodeActionValidationMode.Full, - }; - await test.RunAsync(); - } + }.RunAsync(); - private static async Task TestMissingAsync(string initialMarkup) - => await VerifyCS.VerifyCodeFixAsync(initialMarkup, initialMarkup); + private static Task TestMissingAsync(string initialMarkup) + => VerifyCS.VerifyCodeFixAsync(initialMarkup, initialMarkup); [Fact] - public async Task AddMissingInheritdocOnOverridenMethod() + public async Task AddMissingInheritdocOnOverriddenMethod() { await TestAsync( """ @@ -69,7 +67,7 @@ public override void M() { } [InlineData("public void {|CS1591:OtherMethod|}() { }")] [InlineData("public void {|CS1591:M|}() { }")] [InlineData("public new void {|CS1591:M|}() { }")] - public async Task DoNotOfferOnNotOverridenMethod(string methodDefintion) + public async Task DoNotOfferOnNotOverriddenMethod(string methodDefinition) { await TestMissingAsync( $$""" @@ -82,7 +80,7 @@ public virtual void M() { } /// Some doc. public class Derived: BaseClass { - {{methodDefintion}} + {{methodDefinition}} } """); } @@ -139,7 +137,7 @@ void IInterface.M() { } """); } [Fact] - public async Task AddMissingInheritdocOnOverridenProperty() + public async Task AddMissingInheritdocOnOverriddenProperty() { await TestAsync( """ @@ -269,8 +267,8 @@ public virtual void M() { } /// Some doc. public class Derived: BaseClass { - /// // Comment + /// public override void M() { } } """); @@ -304,9 +302,9 @@ public virtual void M() { } /// Some doc. public class Derived: BaseClass { - /// // Comment 1 - /* Comment 2 */ public /* Comment 3 */ override void M /* Comment 4 */ () /* Comment 5 */ { } /* Comment 6 */ + /* Comment 2 */ /// + public /* Comment 3 */ override void M /* Comment 4 */ () /* Comment 5 */ { } /* Comment 6 */ } """); } @@ -397,4 +395,65 @@ public override void M() { } } """); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/61562")] + public async Task TestOverrideBelowMember() + { + await TestAsync( + """ + using System; + + /// + /// Hello C1 + /// + public abstract class Class1 + { + /// + /// Hello C1.DoStuff + /// + public abstract void DoStuff(); + } + + /// + /// Hello C2 + /// + public class Class2 : Class1 + { + private const int Number = 1; + + public override void {|CS1591:DoStuff|}() + { + throw new NotImplementedException(); + } + } + """, + """ + using System; + + /// + /// Hello C1 + /// + public abstract class Class1 + { + /// + /// Hello C1.DoStuff + /// + public abstract void DoStuff(); + } + + /// + /// Hello C2 + /// + public class Class2 : Class1 + { + private const int Number = 1; + + /// + public override void {|CS1591:DoStuff|}() + { + throw new NotImplementedException(); + } + } + """); + } } diff --git a/src/Analyzers/CSharp/Tests/AssignOutParameters/AssignOutParametersAtStartTests.cs b/src/Analyzers/CSharp/Tests/AssignOutParameters/AssignOutParametersAtStartTests.cs index a9315dcd7cec7..8009c4464e795 100644 --- a/src/Analyzers/CSharp/Tests/AssignOutParameters/AssignOutParametersAtStartTests.cs +++ b/src/Analyzers/CSharp/Tests/AssignOutParameters/AssignOutParametersAtStartTests.cs @@ -514,7 +514,6 @@ char M(bool b, out int i, out int j) return 'a'; } } - char N(bool b, out int i, out int j) { i = 0; @@ -599,7 +598,6 @@ char M(bool b, out int i, out int j) else return 'a'; } - char N(bool b, out int i, out int j) { i = 0; @@ -710,7 +708,6 @@ char M(bool b, out int i, out int j) return 'a'; } } - char N(bool b, out int i, out int j) { i = 0; diff --git a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems index cb7713cf037fb..5a1b4401b4b04 100644 --- a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems +++ b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems @@ -115,6 +115,7 @@ + @@ -184,6 +185,7 @@ + diff --git a/src/Analyzers/CSharp/Tests/ConflictMarkerResolution/ConflictMarkerResolutionTests.cs b/src/Analyzers/CSharp/Tests/ConflictMarkerResolution/ConflictMarkerResolutionTests.cs index 59ff1fc342f65..c843828b45629 100644 --- a/src/Analyzers/CSharp/Tests/ConflictMarkerResolution/ConflictMarkerResolutionTests.cs +++ b/src/Analyzers/CSharp/Tests/ConflictMarkerResolution/ConflictMarkerResolutionTests.cs @@ -717,7 +717,6 @@ class X { await new VerifyCS.Test { TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -734,7 +733,6 @@ class X { await new VerifyCS.Test { TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -751,7 +749,6 @@ class X { await new VerifyCS.Test { TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -1457,7 +1454,6 @@ class X { await new VerifyCS.Test { TestCode = source, - FixedCode = source, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/ConvertNamespace/ConvertToBlockScopedNamespaceAnalyzerTests.cs b/src/Analyzers/CSharp/Tests/ConvertNamespace/ConvertToBlockScopedNamespaceAnalyzerTests.cs index aa486c15513a7..123ccc7a55a15 100644 --- a/src/Analyzers/CSharp/Tests/ConvertNamespace/ConvertToBlockScopedNamespaceAnalyzerTests.cs +++ b/src/Analyzers/CSharp/Tests/ConvertNamespace/ConvertToBlockScopedNamespaceAnalyzerTests.cs @@ -81,7 +81,6 @@ namespace N {} await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { diff --git a/src/Analyzers/CSharp/Tests/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs b/src/Analyzers/CSharp/Tests/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs index dcb3bff374a04..78e14b88c340c 100644 --- a/src/Analyzers/CSharp/Tests/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs +++ b/src/Analyzers/CSharp/Tests/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs @@ -29,7 +29,6 @@ namespace N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, Options = { @@ -49,7 +48,6 @@ namespace N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -115,7 +113,6 @@ namespace N2 await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -138,7 +135,6 @@ namespace N2 await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -160,7 +156,6 @@ namespace N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -182,7 +177,6 @@ namespace N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, ExpectedDiagnostics = { diff --git a/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs b/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs index 298041054b5aa..753c56b4a8cc8 100644 --- a/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs +++ b/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionTests.cs @@ -630,7 +630,6 @@ void M(int i) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = CSharp9, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/FileHeaders/FileHeaderTests.cs b/src/Analyzers/CSharp/Tests/FileHeaders/FileHeaderTests.cs index 70a4e835c508c..fc47c9a461a8a 100644 --- a/src/Analyzers/CSharp/Tests/FileHeaders/FileHeaderTests.cs +++ b/src/Analyzers/CSharp/Tests/FileHeaders/FileHeaderTests.cs @@ -44,7 +44,6 @@ namespace N await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, EditorConfig = $@" [*] {fileHeaderTemplate} @@ -242,7 +241,6 @@ namespace Bar await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, EditorConfig = TestSettings, }.RunAsync(); } @@ -267,7 +265,6 @@ namespace Bar await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, EditorConfig = TestSettings, }.RunAsync(); } @@ -291,7 +288,6 @@ namespace Bar await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, EditorConfig = TestSettings, }.RunAsync(); } @@ -442,7 +438,6 @@ namespace Bar await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, EditorConfig = TestSettings, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/FixReturnType/FixReturnTypeTests.cs b/src/Analyzers/CSharp/Tests/FixReturnType/FixReturnTypeTests.cs index cddfb81f7d86c..dd6daad80c85e 100644 --- a/src/Analyzers/CSharp/Tests/FixReturnType/FixReturnTypeTests.cs +++ b/src/Analyzers/CSharp/Tests/FixReturnType/FixReturnTypeTests.cs @@ -500,7 +500,6 @@ async C M() await new VerifyCS.Test { TestCode = markup, - FixedCode = markup, ReferenceAssemblies = ReferenceAssemblies.Net.Net60 }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.cs b/src/Analyzers/CSharp/Tests/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.cs index 8eec2a263228e..a1da44a8f2dfd 100644 --- a/src/Analyzers/CSharp/Tests/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.cs +++ b/src/Analyzers/CSharp/Tests/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.cs @@ -66,7 +66,6 @@ private static async Task TestRefactoringMissingAsync(string source) await new VerifyRefactoring.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -78,7 +77,6 @@ private static async Task TestCodeFixMissingAsync(string source) await new VerifyCodeFix.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/ImplementInterface/ImplementInterfaceTests.cs b/src/Analyzers/CSharp/Tests/ImplementInterface/ImplementInterfaceTests.cs index 1fcfb16183639..eaa059b5972bd 100644 --- a/src/Analyzers/CSharp/Tests/ImplementInterface/ImplementInterfaceTests.cs +++ b/src/Analyzers/CSharp/Tests/ImplementInterface/ImplementInterfaceTests.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.ImplementInterface; @@ -377,7 +376,7 @@ class Class : IInterface } } """, -codeAction: ("True;False;False:global::IInterface;Microsoft.CodeAnalysis.ImplementInterface.AbstractImplementInterfaceService+ImplementInterfaceCodeAction;", 1)); + codeAction: ("True;False;False:global::IInterface;Microsoft.CodeAnalysis.ImplementInterface.AbstractImplementInterfaceService+ImplementInterfaceCodeAction;", 1)); } [Fact, CompilerTrait(CompilerFeature.Tuples)] @@ -11538,12 +11537,12 @@ class C3 : I1 throw new System.NotImplementedException(); } - public static explicit operator checked string(C3 x) + static explicit I1.operator checked string(C3 x) { throw new System.NotImplementedException(); } - public static explicit operator string(C3 x) + static explicit I1.operator string(C3 x) { throw new System.NotImplementedException(); } @@ -11924,7 +11923,6 @@ event System.EventHandler I.Click await new VerifyCS.Test { TestCode = code, - FixedCode = code, //LanguageVersion = LanguageVersion.CSharp12, ExpectedDiagnostics = { @@ -11934,4 +11932,48 @@ event System.EventHandler I.Click } }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/61263")] + public async Task ImplementStaticConversionsExplicitly() + { + await new VerifyCS.Test + { + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + LanguageVersion = LanguageVersion.CSharp12, + TestCode = """ + interface I11 where T11 : I11 + { + static abstract implicit operator long(T11 x); + static abstract explicit operator int(T11 x); + } + + class C11 : {|CS0535:{|CS0535:I11|}|} + { + } + """, + FixedCode = """ + interface I11 where T11 : I11 + { + static abstract implicit operator long(T11 x); + static abstract explicit operator int(T11 x); + } + + class C11 : I11 + { + static implicit I11.operator long(C11 x) + { + throw new System.NotImplementedException(); + } + + static explicit I11.operator int(C11 x) + { + throw new System.NotImplementedException(); + } + } + """, + CodeActionIndex = 1, + CodeActionEquivalenceKey = "True;False;False:global::I11;Microsoft.CodeAnalysis.ImplementInterface.AbstractImplementInterfaceService+ImplementInterfaceCodeAction;", + CodeActionVerifier = (codeAction, verifier) => verifier.Equal(CodeFixesResources.Implement_all_members_explicitly, codeAction.Title), + }.RunAsync(); + } } diff --git a/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs b/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs index d3e2d6a664fa5..8809c0bf1db00 100644 --- a/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs @@ -52,7 +52,6 @@ void N(Action a) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(); } @@ -82,7 +81,6 @@ void N(Action a) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, Options = { diff --git a/src/Analyzers/CSharp/Tests/MakeMemberRequired/MakeMemberRequiredTests.cs b/src/Analyzers/CSharp/Tests/MakeMemberRequired/MakeMemberRequiredTests.cs index 877aaf5f02adb..3af2b2741c6af 100644 --- a/src/Analyzers/CSharp/Tests/MakeMemberRequired/MakeMemberRequiredTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeMemberRequired/MakeMemberRequiredTests.cs @@ -135,7 +135,6 @@ class MyClass await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11, ReferenceAssemblies = ReferenceAssemblies.Net.Net60, }.RunAsync(); @@ -204,7 +203,6 @@ class MyClass await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11, ReferenceAssemblies = ReferenceAssemblies.Net.Net70 }.RunAsync(); @@ -453,7 +451,6 @@ class MyClass await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, ReferenceAssemblies = ReferenceAssemblies.Net.Net70 }.RunAsync(); @@ -475,7 +472,6 @@ class MyClass await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11, ReferenceAssemblies = ReferenceAssemblies.Net.Net70 }.RunAsync(); @@ -496,7 +492,6 @@ class MyClass await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11, ReferenceAssemblies = ReferenceAssemblies.Net.Net70 }.RunAsync(); diff --git a/src/Analyzers/CSharp/Tests/MakeMethodSynchronous/MakeMethodSynchronousTests.cs b/src/Analyzers/CSharp/Tests/MakeMethodSynchronous/MakeMethodSynchronousTests.cs index 899e883712fe8..5747aa4eff078 100644 --- a/src/Analyzers/CSharp/Tests/MakeMethodSynchronous/MakeMethodSynchronousTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeMethodSynchronous/MakeMethodSynchronousTests.cs @@ -16,9 +16,10 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.MakeMethodSynchronous; EmptyDiagnosticAnalyzer, CSharpMakeMethodSynchronousCodeFixProvider>; -public class MakeMethodSynchronousTests +[Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] +public sealed class MakeMethodSynchronousTests { - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestTaskReturnType() { await VerifyCS.VerifyCodeFixAsync( @@ -44,7 +45,7 @@ void Goo() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestTaskOfTReturnType() { await VerifyCS.VerifyCodeFixAsync( @@ -72,7 +73,7 @@ class C """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestSecondModifier() { await VerifyCS.VerifyCodeFixAsync( @@ -98,7 +99,7 @@ public void Goo() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestFirstModifier() { await VerifyCS.VerifyCodeFixAsync( @@ -124,7 +125,7 @@ public void Goo() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestTrailingTrivia() { await VerifyCS.VerifyCodeFixAsync( @@ -151,7 +152,7 @@ void Goo() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestRenameMethod() { await VerifyCS.VerifyCodeFixAsync( @@ -177,7 +178,7 @@ void Goo() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestRenameMethod1() { await VerifyCS.VerifyCodeFixAsync( @@ -213,7 +214,7 @@ void Bar() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestParenthesizedLambda() { var source = @@ -260,7 +261,7 @@ void Goo() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestSimpleLambda() { var source = @@ -307,7 +308,7 @@ void Goo() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestLambdaWithExpressionBody() { var source = @@ -356,7 +357,7 @@ void Goo() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestAnonymousMethod() { var source = @@ -403,7 +404,7 @@ void Goo() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestFixAll() { await VerifyCS.VerifyCodeFixAsync( @@ -443,7 +444,7 @@ void Goo() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] public async Task TestRemoveAwaitFromCaller1() { @@ -492,7 +493,7 @@ void Goo() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] public async Task TestRemoveAwaitFromCaller2() { @@ -541,7 +542,7 @@ void Goo() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] public async Task TestRemoveAwaitFromCaller3() { @@ -590,7 +591,7 @@ void Goo() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] public async Task TestRemoveAwaitFromCaller4() { @@ -639,7 +640,7 @@ void Goo() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] public async Task TestRemoveAwaitFromCallerNested1() { @@ -690,7 +691,7 @@ int Goo(int i) }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] public async Task TestRemoveAwaitFromCallerNested() { @@ -861,7 +862,7 @@ class C """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task MethodWithUsingAwait() { var source = @@ -890,7 +891,7 @@ async System.Threading.Tasks.Task MAsync() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task MethodWithUsingNoAwait() { await VerifyCS.VerifyCodeFixAsync( @@ -920,7 +921,7 @@ void M() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task MethodWithAwaitForEach() { var source = @@ -943,7 +944,7 @@ await VerifyCS.VerifyCodeFixAsync( source); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task MethodWithForEachNoAwait() { await VerifyCS.VerifyCodeFixAsync( @@ -971,7 +972,7 @@ void M() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task MethodWithForEachVariableAwait() { var source = @@ -994,7 +995,7 @@ await VerifyCS.VerifyCodeFixAsync( source); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task MethodWithForEachVariableNoAwait() { await VerifyCS.VerifyCodeFixAsync( @@ -1022,7 +1023,7 @@ void M() """); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestIAsyncEnumerableReturnType() { var source = @@ -1060,7 +1061,7 @@ IEnumerable M() }.RunAsync(); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] + [Fact] public async Task TestIAsyncEnumeratorReturnTypeOnLocalFunction() { var source = diff --git a/src/Analyzers/CSharp/Tests/MakeStructMemberReadOnly/MakeStructMemberReadOnlyTests.cs b/src/Analyzers/CSharp/Tests/MakeStructMemberReadOnly/MakeStructMemberReadOnlyTests.cs index d607fc390888c..9b914d0ea1da3 100644 --- a/src/Analyzers/CSharp/Tests/MakeStructMemberReadOnly/MakeStructMemberReadOnlyTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeStructMemberReadOnly/MakeStructMemberReadOnlyTests.cs @@ -53,7 +53,6 @@ void M() { } await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -69,7 +68,6 @@ void M() { } await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -85,7 +83,6 @@ readonly void M() { } await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -104,7 +101,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -125,7 +121,6 @@ static void G(ref S s) { } await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -149,7 +144,6 @@ public static void G(ref this S s) { } await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -295,7 +289,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -315,7 +308,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -337,7 +329,6 @@ static void G(ref int x) { } await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -359,7 +350,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -379,7 +369,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -403,7 +392,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -428,7 +416,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -453,7 +440,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -469,7 +455,6 @@ void M() { } await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp7, }.RunAsync(); } @@ -507,7 +492,6 @@ struct S await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -919,7 +903,6 @@ unsafe void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -944,7 +927,6 @@ void X() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -966,7 +948,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -991,7 +972,6 @@ void X() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -1016,7 +996,6 @@ void X() await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -1167,7 +1146,6 @@ void M() await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, }.RunAsync(); } @@ -1194,7 +1172,6 @@ void Dispose() await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, }.RunAsync(); } @@ -1252,7 +1229,6 @@ struct T where X : IComparable await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, }.RunAsync(); } @@ -1270,7 +1246,6 @@ struct T where X : struct, IComparable await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, }.RunAsync(); } @@ -1310,7 +1285,6 @@ struct S await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -1329,7 +1303,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } @@ -1348,7 +1321,6 @@ void M() await new VerifyCS.Test { TestCode = test, - FixedCode = test, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/MakeTypePartial/MakeTypePartialTests.cs b/src/Analyzers/CSharp/Tests/MakeTypePartial/MakeTypePartialTests.cs index 377704d006a21..f58d5f83e2b14 100644 --- a/src/Analyzers/CSharp/Tests/MakeTypePartial/MakeTypePartialTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeTypePartial/MakeTypePartialTests.cs @@ -376,7 +376,6 @@ namespace TestNamespace2 await new VerifyCS.Test { TestCode = markup, - FixedCode = markup, LanguageVersion = LanguageVersion.CSharp10 }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/NamingStyles/NamingStylesTests.cs b/src/Analyzers/CSharp/Tests/NamingStyles/NamingStylesTests.cs index ee726f9e1485d..c184e762edd05 100644 --- a/src/Analyzers/CSharp/Tests/NamingStyles/NamingStylesTests.cs +++ b/src/Analyzers/CSharp/Tests/NamingStyles/NamingStylesTests.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -21,14 +19,10 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.NamingStyles; [Trait(Traits.Feature, Traits.Features.NamingStyle)] -public class NamingStylesTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor +public sealed class NamingStylesTests(ITestOutputHelper logger) + : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor(logger) { - public NamingStylesTests(ITestOutputHelper logger) - : base(logger) - { - } - - private static readonly NamingStylesTestOptionSets s_options = new NamingStylesTestOptionSets(LanguageNames.CSharp); + private static readonly NamingStylesTestOptionSets s_options = new(LanguageNames.CSharp); internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (new CSharpNamingStyleDiagnosticAnalyzer(), new NamingStyleCodeFixProvider()); @@ -1402,6 +1396,34 @@ internal interface """, new TestParameters(options: s_options.InterfaceNamesStartWithI)); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17656")] + public async Task TestInterfacesStartWithIOnTypeThatAlreadyStartsWithI1() + { + await TestInRegularAndScript1Async(""" + interface [|InputStream|] { } + """, """ + interface IInputStream { } + """, new TestParameters(options: s_options.InterfaceNamesStartWithI)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17656")] + public async Task TestInterfacesStartWithIOnTypeThatAlreadyStartsWithI2() + { + await TestInRegularAndScript1Async(""" + interface [|Stream|] { } + """, """ + interface IStream { } + """, new TestParameters(options: s_options.InterfaceNamesStartWithI)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17656")] + public async Task TestInterfacesStartWithIOnTypeThatAlreadyStartsWithI3() + { + await TestMissingInRegularAndScriptAsync(""" + interface [|IInputStream|] { } + """, new TestParameters(options: s_options.InterfaceNamesStartWithI)); + } + #if CODE_STYLE [Fact(Skip = "https://github.com/dotnet/roslyn/issues/42218")] #else diff --git a/src/Analyzers/CSharp/Tests/NewLines/ArrowExpressionClausePlacement/ArrowExpressionClausePlacementTests.cs b/src/Analyzers/CSharp/Tests/NewLines/ArrowExpressionClausePlacement/ArrowExpressionClausePlacementTests.cs index 46710ac1f8dbe..f1a7c333867f5 100644 --- a/src/Analyzers/CSharp/Tests/NewLines/ArrowExpressionClausePlacement/ArrowExpressionClausePlacementTests.cs +++ b/src/Analyzers/CSharp/Tests/NewLines/ArrowExpressionClausePlacement/ArrowExpressionClausePlacementTests.cs @@ -34,7 +34,6 @@ public int Add() => await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -53,7 +52,6 @@ class C await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -72,7 +70,6 @@ class C await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -94,7 +91,6 @@ public void Main() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -119,7 +115,6 @@ public void Goo(System.Func action) { } await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -171,7 +166,6 @@ public int Add await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -255,7 +249,6 @@ public int Add(int{|CS1001:)|} => await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -275,7 +268,6 @@ public int Add() => await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -295,7 +287,6 @@ public int Add() => await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -317,7 +308,6 @@ public int Add() => await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -339,7 +329,6 @@ public int Add() => await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInArrowExpressionClause, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/NewLines/ConditionalExpressionPlacement/ConditionalExpressionPlacementTests.cs b/src/Analyzers/CSharp/Tests/NewLines/ConditionalExpressionPlacement/ConditionalExpressionPlacementTests.cs index b4e535b6ca768..de48866e2e01c 100644 --- a/src/Analyzers/CSharp/Tests/NewLines/ConditionalExpressionPlacement/ConditionalExpressionPlacementTests.cs +++ b/src/Analyzers/CSharp/Tests/NewLines/ConditionalExpressionPlacement/ConditionalExpressionPlacementTests.cs @@ -37,7 +37,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -98,7 +97,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -122,7 +120,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -146,7 +143,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -169,7 +165,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -192,7 +187,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -215,7 +209,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -241,7 +234,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -267,7 +259,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -293,7 +284,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -319,7 +309,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -345,7 +334,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterTokenInConditionalExpression, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/NewLines/ConsecutiveBracePlacement/ConsecutiveBracePlacementTests.cs b/src/Analyzers/CSharp/Tests/NewLines/ConsecutiveBracePlacement/ConsecutiveBracePlacementTests.cs index 0fffe3768cf00..47f074cc96a13 100644 --- a/src/Analyzers/CSharp/Tests/NewLines/ConsecutiveBracePlacement/ConsecutiveBracePlacementTests.cs +++ b/src/Analyzers/CSharp/Tests/NewLines/ConsecutiveBracePlacement/ConsecutiveBracePlacementTests.cs @@ -26,7 +26,6 @@ public async Task NotForBracesOnSameLineDirectlyTouching() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -40,7 +39,6 @@ public async Task NotForBracesOnSameLineWithSpace() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -54,7 +52,6 @@ public async Task NotForBracesOnSameLineWithComment() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -68,7 +65,6 @@ public async Task NotForBracesOnSameLineWithCommentAndSpaces() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -89,7 +85,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -110,7 +105,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -131,7 +125,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -155,7 +148,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -179,7 +171,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -203,7 +194,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -227,7 +217,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -251,7 +240,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -275,7 +263,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); @@ -303,7 +290,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -330,7 +316,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -357,7 +342,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); @@ -411,7 +395,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.TrueWithSuggestionEnforcement } } }.RunAsync(); } @@ -817,7 +800,6 @@ internal interface IOption2 await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); @@ -864,7 +846,6 @@ internal interface IOption2 await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, Options = { { CSharpCodeStyleOptions.AllowBlankLinesBetweenConsecutiveBraces, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); diff --git a/src/Analyzers/CSharp/Tests/NewLines/ConsecutiveStatementPlacement/ConsecutiveStatementPlacementTests.cs b/src/Analyzers/CSharp/Tests/NewLines/ConsecutiveStatementPlacement/ConsecutiveStatementPlacementTests.cs index 8b7a4122d28b4..744d87aad7170 100644 --- a/src/Analyzers/CSharp/Tests/NewLines/ConsecutiveStatementPlacement/ConsecutiveStatementPlacementTests.cs +++ b/src/Analyzers/CSharp/Tests/NewLines/ConsecutiveStatementPlacement/ConsecutiveStatementPlacementTests.cs @@ -32,7 +32,6 @@ class C await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -52,7 +51,6 @@ void Y() { } await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -74,7 +72,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -96,7 +93,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -121,7 +117,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -146,7 +141,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -172,7 +166,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -198,7 +191,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -224,7 +216,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -250,7 +241,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -277,7 +267,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -303,7 +292,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -333,7 +321,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -391,7 +378,6 @@ void M() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -941,7 +927,6 @@ public void TestTernaryConstruction() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowStatementImmediatelyAfterBlock, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/NewLines/ConstructorInitializerPlacement/ConstructorInitializerPlacementTests.cs b/src/Analyzers/CSharp/Tests/NewLines/ConstructorInitializerPlacement/ConstructorInitializerPlacementTests.cs index da77078eaf284..a65f1879335f7 100644 --- a/src/Analyzers/CSharp/Tests/NewLines/ConstructorInitializerPlacement/ConstructorInitializerPlacementTests.cs +++ b/src/Analyzers/CSharp/Tests/NewLines/ConstructorInitializerPlacement/ConstructorInitializerPlacementTests.cs @@ -34,7 +34,6 @@ public C() : await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer, CodeStyleOption2.TrueWithSilentEnforcement } } }.RunAsync(); } @@ -88,7 +87,6 @@ public C() : base() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -110,7 +108,6 @@ public C() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -132,7 +129,6 @@ public C() : //comment await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -258,7 +254,6 @@ public C() : await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/NewLines/EmbeddedStatementPlacement/EmbeddedStatementPlacementTests.cs b/src/Analyzers/CSharp/Tests/NewLines/EmbeddedStatementPlacement/EmbeddedStatementPlacementTests.cs index 8388ab43f13c9..cda350f8e7e79 100644 --- a/src/Analyzers/CSharp/Tests/NewLines/EmbeddedStatementPlacement/EmbeddedStatementPlacementTests.cs +++ b/src/Analyzers/CSharp/Tests/NewLines/EmbeddedStatementPlacement/EmbeddedStatementPlacementTests.cs @@ -33,7 +33,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.AllowEmbeddedStatementsOnSameLine, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -106,7 +105,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.AllowEmbeddedStatementsOnSameLine, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -202,7 +200,6 @@ int Prop2 await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.AllowEmbeddedStatementsOnSameLine, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/NewLines/MultipleBlankLines/MultipleBlankLinesTests.cs b/src/Analyzers/CSharp/Tests/NewLines/MultipleBlankLines/MultipleBlankLinesTests.cs index b1dcc6edc3e62..e997fbbbeef2c 100644 --- a/src/Analyzers/CSharp/Tests/NewLines/MultipleBlankLines/MultipleBlankLinesTests.cs +++ b/src/Analyzers/CSharp/Tests/NewLines/MultipleBlankLines/MultipleBlankLinesTests.cs @@ -27,7 +27,6 @@ public async Task TestOneBlankLineAtTopOfFile() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -62,7 +61,6 @@ public async Task TestTwoBlankLineAtTopOfFile_NotWithOptionOff() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.TrueWithSuggestionEnforcement } } }.RunAsync(); } @@ -118,7 +116,6 @@ public async Task TestOneBlankLineAtTopOfEmptyFile() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -194,7 +191,6 @@ public async Task TestNoBlankLineAtEndOfFile_1() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -211,7 +207,6 @@ public async Task TestNoBlankLineAtEndOfFile_2() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -229,7 +224,6 @@ public async Task TestOneBlankLineAtEndOfFile() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -323,7 +317,6 @@ public async Task TestNoBlankLineBetweenTokens() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -340,7 +333,6 @@ public async Task TestOneBlankLineBetweenTokens() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -429,7 +421,6 @@ public async Task TestNoBlankLineAfterComment() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -447,7 +438,6 @@ public async Task TestOneBlankLineAfterComment() await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -541,7 +531,6 @@ public async Task TestNoBlankLineAfterDirective() await new Verify.Test { TestCode = code, - FixedCode = code, LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); @@ -560,7 +549,6 @@ public async Task TestOneBlankLineAfterDirective() await new Verify.Test { TestCode = code, - FixedCode = code, LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); @@ -659,7 +647,6 @@ class C await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -678,7 +665,6 @@ class C await new Verify.Test { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); } @@ -781,7 +767,6 @@ class C await new Verify.Test { TestCode = code, - FixedCode = code, LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); @@ -805,7 +790,6 @@ class C await new Verify.Test { TestCode = code, - FixedCode = code, LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, Options = { { CodeStyleOptions2.AllowMultipleBlankLines, CodeStyleOption2.FalseWithSuggestionEnforcement } } }.RunAsync(); diff --git a/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs b/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs index 147d114808c47..99fad10dcca2d 100644 --- a/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.RemoveAsyncModifier; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; @@ -17,7 +18,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RemoveAsyncModifier; CSharpRemoveAsyncModifierCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsRemoveAsyncModifier)] -public class RemoveAsyncModifierTests : CodeAnalysis.CSharp.Test.Utilities.CSharpTestBase +public sealed class RemoveAsyncModifierTests { [Fact] public async Task Method_Task_MultipleAndNested() @@ -917,7 +918,7 @@ public void M1() } [Fact] - public async Task ParenthesisedLambda_TaskOfT_ExpressionBody() + public async Task ParenthesizedLambda_TaskOfT_ExpressionBody() { await VerifyCS.VerifyCodeFixAsync( """ @@ -947,7 +948,7 @@ public void M1() } [Fact] - public async Task ParenthesisedLambda_TaskOfT_BlockBody() + public async Task ParenthesizedLambda_TaskOfT_BlockBody() { await VerifyCS.VerifyCodeFixAsync( """ @@ -982,7 +983,7 @@ public void M1() } [Fact] - public async Task ParenthesisedLambda_Task_ExpressionBody() + public async Task ParenthesizedLambda_Task_ExpressionBody() { await VerifyCS.VerifyCodeFixAsync( """ @@ -1012,7 +1013,7 @@ public void M1() } [Fact] - public async Task ParenthesisedLambda_Task_BlockBody() + public async Task ParenthesizedLambda_Task_BlockBody() { await VerifyCS.VerifyCodeFixAsync( """ @@ -1163,7 +1164,7 @@ async IAsyncEnumerable M() yield return 1; } } - """ + AsyncStreamsTypes; + """ + CSharpTestBase.AsyncStreamsTypes; await new VerifyCS.Test { @@ -1207,7 +1208,7 @@ async void M() } [Fact] - public async Task ParenthesisedLambda_AsyncVoid_Missing() + public async Task ParenthesizedLambda_AsyncVoid_Missing() { var source = """ using System; @@ -1263,4 +1264,38 @@ void M() FixedCode = source, }.RunAsync(); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65380")] + public async Task TestCloseBraceTrivia() + { + await VerifyCS.VerifyCodeFixAsync( + """ + using System; + using System.Threading.Tasks; + + public class Class1 + { + public async Task {|CS1998:Goo|}() + { + //Hello + Console.WriteLine("Goo"); + //World + } + } + """, + """ + using System; + using System.Threading.Tasks; + + public class Class1 + { + public Task Goo() + { + //Hello + Console.WriteLine("Goo"); + return Task.CompletedTask; + //World + } + } + """); + } } diff --git a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryCast/RemoveUnnecessaryCastTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryCast/RemoveUnnecessaryCastTests.cs index ca6f3fe43ab08..0c7ae7293a84d 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryCast/RemoveUnnecessaryCastTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryCast/RemoveUnnecessaryCastTests.cs @@ -5741,7 +5741,6 @@ void Goo() await new VerifyCS.Test { TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -5766,7 +5765,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -5792,7 +5790,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -5818,7 +5815,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -5910,7 +5906,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -5936,7 +5931,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -5962,7 +5956,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -6064,7 +6057,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -6086,7 +6078,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -6108,7 +6099,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7_1, }.RunAsync(); } @@ -6164,7 +6154,6 @@ void M(string s) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -6188,7 +6177,6 @@ void M(string s) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -8238,7 +8226,6 @@ static int Main() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -8311,7 +8298,6 @@ void M(bool x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(); } @@ -8360,7 +8346,6 @@ void M(bool x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(); } @@ -8409,7 +8394,6 @@ void M(bool x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -8509,7 +8493,6 @@ void M(bool x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(); } @@ -8558,7 +8541,6 @@ void M(bool x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(); } @@ -8607,7 +8589,6 @@ void M(bool x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(); } @@ -9659,7 +9640,6 @@ public class C { var test = new VerifyCS.Test() { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9 }; @@ -9711,7 +9691,6 @@ public class C { var test = new VerifyCS.Test() { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9 }; @@ -9733,7 +9712,6 @@ public class C { var test = new VerifyCS.Test() { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9 }; @@ -9819,7 +9797,6 @@ public static ulong P(ulong a, uint b) var test = new VerifyCS.Test() { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9 }; @@ -9843,7 +9820,6 @@ public static nuint N(nuint a, uint b) var test = new VerifyCS.Test() { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9 }; @@ -9867,7 +9843,6 @@ public static ulong N() var test = new VerifyCS.Test() { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9 }; @@ -10004,7 +9979,6 @@ static void Main() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -10033,7 +10007,6 @@ public E First await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -10057,7 +10030,6 @@ public TestClass(Func value) { } await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -10080,7 +10052,6 @@ unsafe void M(nint** ptr) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -10916,7 +10887,6 @@ void M(object o) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -12120,7 +12090,6 @@ void AddHandler(Delegate handler) { } await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = version, }.RunAsync(); } @@ -12151,7 +12120,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12171,7 +12139,6 @@ class C await new VerifyCS.Test { TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -12189,7 +12156,6 @@ class C await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12214,7 +12180,6 @@ static IEnumerable DoThis(IEnumerable notreallynull) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12237,7 +12202,6 @@ bool M(object obj) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12259,7 +12223,6 @@ public IEnumerable Bar() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12281,7 +12244,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12305,7 +12267,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12328,7 +12289,6 @@ public static void MethodName() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12345,7 +12305,6 @@ static class Program await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12368,7 +12327,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12393,7 +12351,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12413,7 +12370,6 @@ public uint fn1(sbyte a, sbyte b) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12433,7 +12389,6 @@ public void fn1(int start, int end) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12458,7 +12413,6 @@ public Code(DoSomething f) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12482,7 +12436,6 @@ static void CheckRedundantCast() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12514,7 +12467,6 @@ enum Numbers await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12565,7 +12517,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12607,7 +12558,6 @@ void Run() await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12635,7 +12585,6 @@ public X(double d) { } await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12683,7 +12632,6 @@ private C(ReadOnlyMemory? buffer = null) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -12729,7 +12677,6 @@ public unsafe static implicit operator PointerDelegate(delegate*(object o) where T : Delegate await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -13128,7 +13072,6 @@ public enum Test await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryImports/RemoveUnnecessaryImportsTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryImports/RemoveUnnecessaryImportsTests.cs index d06499cad9610..de8563ded1116 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryImports/RemoveUnnecessaryImportsTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryImports/RemoveUnnecessaryImportsTests.cs @@ -19,8 +19,10 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RemoveUnnecessaryImport CSharpRemoveUnnecessaryImportsCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryImports)] -public class RemoveUnnecessaryImportsTests +public sealed class RemoveUnnecessaryImportsTests { + private static readonly string s_tab = "\t"; + [Fact] public async Task TestNoReferences() { @@ -186,22 +188,9 @@ public async Task TestGeneratedCode() var source = """ // - [|{|IDE0005_gen:using System;|} - using System.Collections.Generic; - {|IDE0005_gen:using System.Linq;|}|] - - class Program - { - static void Main(string[] args) - { - List d; - } - } - """; - var fixedSource = """ - // - + using System; using System.Collections.Generic; + using System.Linq; class Program { @@ -212,18 +201,9 @@ static void Main(string[] args) } """; - // Fix All operations in generated code do not apply changes - var batchFixedSource = source; - await new VerifyCS.Test { TestCode = source, - FixedCode = fixedSource, - BatchFixedState = - { - Sources = { batchFixedSource }, - MarkupHandling = MarkupMode.Allow, - }, }.RunAsync(); } @@ -2268,4 +2248,36 @@ static void Main(string[] args) """ }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65114")] + public async Task DoNotTouchInnerNamespaceWithoutUsings() + { + await VerifyCS.VerifyCodeFixAsync( + $$""" + [|{|IDE0005:using System; + using System.Collections.Generic; + using System.Linq;|}|] + + namespace N + { + {{s_tab}}class Program + { + static void Main(string[] args) + { + } + } + } + """, + $$""" + namespace N + { + {{s_tab}}class Program + { + static void Main(string[] args) + { + } + } + } + """); + } } diff --git a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryLambdaExpression/RemoveUnnecessaryLambdaExpressionTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryLambdaExpression/RemoveUnnecessaryLambdaExpressionTests.cs index 7bdc80f506142..2f9823acb18b3 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryLambdaExpression/RemoveUnnecessaryLambdaExpressionTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryLambdaExpression/RemoveUnnecessaryLambdaExpressionTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp; @@ -19,11 +20,11 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RemoveUnnecessaryLambda CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryLambdaExpression)] -public class RemoveUnnecessaryLambdaExpressionTests +public sealed class RemoveUnnecessaryLambdaExpressionTests { private static async Task TestInRegularAndScriptAsync( - string testCode, - string fixedCode, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string testCode, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string fixedCode, LanguageVersion version = LanguageVersion.CSharp12, OutputKind? outputKind = null) { @@ -40,7 +41,7 @@ private static async Task TestInRegularAndScriptAsync( } private static Task TestMissingInRegularAndScriptAsync( - string testCode, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string testCode, LanguageVersion version = LanguageVersion.CSharp12, OutputKind? outputKind = null) => TestInRegularAndScriptAsync(testCode, testCode, version, outputKind); @@ -119,7 +120,6 @@ void Bar(Func f) { } await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp12, Options = { { CSharpCodeStyleOptions.PreferMethodGroupConversion, new CodeStyleOption2(false, NotificationOption2.None) } } }.RunAsync(); @@ -2035,4 +2035,44 @@ public string ToStr(int x) } """); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71300")] + public async Task PreserveComment() + { + await TestInRegularAndScriptAsync(""" + using System; + + class C + { + void M1() + { + M2([|() => + { + // I hope M2 doesn't call M1! + |]M1(); + }); + } + + void M2(Action a) + { + } + } + """, + """ + using System; + + class C + { + void M1() + { + // I hope M2 doesn't call M1! + M2(M1); + } + + void M2(Action a) + { + } + } + """); + } } diff --git a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryExpressionParenthesesTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryExpressionParenthesesTests.cs index a8d90e34f4f95..f7b5eca75f4ad 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryExpressionParenthesesTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryExpressionParenthesesTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; @@ -20,17 +21,16 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RemoveUnnecessaryParentheses; [Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)] -public partial class RemoveUnnecessaryExpressionParenthesesTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor +public sealed class RemoveUnnecessaryExpressionParenthesesTests(ITestOutputHelper logger) + : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor(logger) { - public RemoveUnnecessaryExpressionParenthesesTests(ITestOutputHelper logger) - : base(logger) - { - } - internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (new CSharpRemoveUnnecessaryExpressionParenthesesDiagnosticAnalyzer(), new CSharpRemoveUnnecessaryParenthesesCodeFixProvider()); - private async Task TestAsync(string initial, string expected, bool offeredWhenRequireForClarityIsEnabled, int index = 0) + private async Task TestAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string initial, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string expected, + bool offeredWhenRequireForClarityIsEnabled, int index = 0) { await TestInRegularAndScriptAsync(initial, expected, options: RemoveAllUnnecessaryParentheses, index: index); @@ -3458,4 +3458,28 @@ public void M() } """); } + + [Fact] + public async Task TestRemoveAroundCollectionExpression() + { + await TestInRegularAndScriptAsync( + """ + class C + { + void M(bool b) + { + int[] a = b ? $$([1]) : []; + } + } + """, + """ + class C + { + void M(bool b) + { + int[] a = b ? [1] : []; + } + } + """); + } } diff --git a/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs index c06801cfe9adc..09671f55509d9 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.cs @@ -15,7 +15,6 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RemoveUnusedMembers; -using static Microsoft.CodeAnalysis.CSharp.UsePatternCombinators.AnalyzedPattern; using VerifyCS = CSharpCodeFixVerifier< CSharpRemoveUnusedMembersDiagnosticAnalyzer, CSharpRemoveUnusedMembersCodeFixProvider>; @@ -439,7 +438,6 @@ public async Task EntryPointMethodNotFlagged_06() await new VerifyCS.Test { TestCode = code, - FixedCode = code, ExpectedDiagnostics = { // /0/Test0.cs(2,1): error CS8805: Program using top-level statements must be an executable. @@ -3243,4 +3241,102 @@ class C await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/57470")] + public async Task TestForeach() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections; + + static class Program + { + static void Main(string[] args) + { + foreach (var i in 1..10) + Console.WriteLine(i); + } + + static IEnumerator GetEnumerator(this Range range) + { + for (int i = range.Start.Value; i < range.End.Value; i++) + yield return i; + } + } + """, + LanguageVersion = LanguageVersion.CSharp13, + ReferenceAssemblies = ReferenceAssemblies.Net.Net90, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75995")] + public async Task KeepUsedDeconstructMethod() + { + await new VerifyCS.Test + { + TestCode = """ + #nullable enable + + class C + { + public void M( + ref object? o, + ref object? p) + { + (o, p) = this; + } + + void Deconstruct( + out object? o, + out object? p) + { + o = null; + p = null; + } + } + """, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75995")] + public async Task RemoveUnusedDeconstructMethod() + { + await new VerifyCS.Test + { + TestCode = """ + #nullable enable + + class C + { + public void M( + ref object o, + ref object p) + { + } + + void [|Deconstruct|]( + out object? o, + out object? p) + { + o = null; + p = null; + } + } + """, + FixedCode = """ + #nullable enable + + class C + { + public void M( + ref object o, + ref object p) + { + } + } + """, + }.RunAsync(); + } } diff --git a/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs index c80e2492244f8..48b3eab5b066c 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs @@ -65,7 +65,6 @@ int M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable, NotificationOption2.None }, @@ -93,7 +92,6 @@ int M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable, NotificationOption2.None }, @@ -406,7 +404,6 @@ void M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.UnusedLocalVariable }, @@ -1317,7 +1314,6 @@ int M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option }, @@ -1401,7 +1397,6 @@ int M(int x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option }, @@ -1525,7 +1520,6 @@ int M(int x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option }, @@ -1555,7 +1549,6 @@ bool M(bool x) await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option }, @@ -1586,7 +1579,6 @@ bool M() await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, (UnusedValuePreference)option }, @@ -10063,7 +10055,6 @@ public void Reset() { await new VerifyCS.Test { TestCode = source, - FixedCode = source, Options = { { CSharpCodeStyleOptions.UnusedValueAssignment, UnusedValuePreference.DiscardVariable }, @@ -10072,4 +10063,33 @@ public void Reset() { ReferenceAssemblies = ReferenceAssemblies.Net.Net80, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72829")] + public async Task RemoveRedundantAssignment_PreservesUsingVar() + { + await TestInRegularAndScriptAsync( + """ + class C + { + void M() + { + {|FixAllInDocument:int items = 0;|} + items = 42; + System.Console.WriteLine(items); + using var _ = System.IO.File.OpenRead("test.txt"); + } + } + """, + """ + class C + { + void M() + { + int items = 42; + System.Console.WriteLine(items); + using var _ = System.IO.File.OpenRead("test.txt"); + } + } + """); + } } diff --git a/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionFixAllTests.cs b/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionFixAllTests.cs index 774562e8c2c98..4cd70149a36e7 100644 --- a/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionFixAllTests.cs +++ b/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionFixAllTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.SimplifyLinqExpression; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.SimplifyLinqExpression; using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; @@ -12,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Analyzers.UnitTests.SimplifyLinqExpressi using VerifyCS = CSharpCodeFixVerifier< CSharpSimplifyLinqExpressionDiagnosticAnalyzer, - CSharpSimplifyLinqExpressionCodeFixProvider>; + SimplifyLinqExpressionCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsInlineDeclaration)] public partial class CSharpSimplifyLinqExpressionTests diff --git a/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionTests.cs b/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionTests.cs index 6e5d611754ff4..7d340c5d7f1ce 100644 --- a/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionTests.cs +++ b/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqExpressionTests.cs @@ -5,17 +5,19 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.SimplifyLinqExpression; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.SimplifyLinqExpression; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.Analyzers.UnitTests.SimplifyLinqExpression; using VerifyCS = CSharpCodeFixVerifier< CSharpSimplifyLinqExpressionDiagnosticAnalyzer, - CSharpSimplifyLinqExpressionCodeFixProvider>; + SimplifyLinqExpressionCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsSimplifyLinqExpression)] -public partial class CSharpSimplifyLinqExpressionTests +public sealed partial class CSharpSimplifyLinqExpressionTests { [Theory, CombinatorialData] public static async Task TestAllowedMethodTypes( @@ -525,4 +527,40 @@ void Main() """; await VerifyCS.VerifyAnalyzerAsync(source); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52283")] + public static async Task TestTrivia1() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Linq; + using System.Collections.Generic; + + class C + { + static void Main(string[] args) + { + var v = [|args.Skip(1) + .Where(a => a.Length == 1).Count()|]; + } + } + """, + FixedCode = """ + using System; + using System.Linq; + using System.Collections.Generic; + + class C + { + static void Main(string[] args) + { + var v = args.Skip(1) + .Count(a => a.Length == 1); + } + } + """ + }.RunAsync(); + } } diff --git a/src/Analyzers/CSharp/Tests/UseAutoProperty/UseAutoPropertyTests.cs b/src/Analyzers/CSharp/Tests/UseAutoProperty/UseAutoPropertyTests.cs index c819c4aad6ad3..96306663905c7 100644 --- a/src/Analyzers/CSharp/Tests/UseAutoProperty/UseAutoPropertyTests.cs +++ b/src/Analyzers/CSharp/Tests/UseAutoProperty/UseAutoPropertyTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.CSharp.UseAutoProperty; using Microsoft.CodeAnalysis.Diagnostics; @@ -1877,7 +1878,7 @@ class Class [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23216")] [WorkItem("https://github.com/dotnet/roslyn/issues/23215")] - public async Task TestFixAllInDocument() + public async Task TestFixAllInDocument1() { await TestInRegularAndScript1Async( """ @@ -1914,6 +1915,53 @@ class Class """); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26527")] + public async Task TestFixAllInDocument2() + { + await TestInRegularAndScript1Async( + """ + internal struct StringFormat + { + private readonly object {|FixAllInDocument:_argument1|}; + private readonly object _argument2; + private readonly object _argument3; + private readonly object[] _arguments; + + public object Argument1 + { + get { return _argument1; } + } + + public object Argument2 + { + get { return _argument2; } + } + + public object Argument3 + { + get { return _argument3; } + } + + public object[] Arguments + { + get { return _arguments; } + } + } + """, + """ + internal struct StringFormat + { + public object Argument1 { get; } + + public object Argument2 { get; } + + public object Argument3 { get; } + + public object[] Arguments { get; } + } + """); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23735")] public async Task ExplicitInterfaceImplementationGetterOnly() { diff --git a/src/Analyzers/CSharp/Tests/UseAutoProperty/UseAutoPropertyTests_Field.cs b/src/Analyzers/CSharp/Tests/UseAutoProperty/UseAutoPropertyTests_Field.cs index f738c92812ca5..85687a5791c4b 100644 --- a/src/Analyzers/CSharp/Tests/UseAutoProperty/UseAutoPropertyTests_Field.cs +++ b/src/Analyzers/CSharp/Tests/UseAutoProperty/UseAutoPropertyTests_Field.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; @@ -68,6 +69,38 @@ string P """, parseOptions: Preview); } + [Fact] + public async Task TestFieldWithInitializer() + { + await TestInRegularAndScriptAsync( + """ + class Class + { + [|string s = ""|]; + + string P + { + get + { + return s.Trim(); + } + } + } + """, + """ + class Class + { + string P + { + get + { + return field.Trim(); + } + } = ""; + } + """, parseOptions: Preview); + } + [Fact] public async Task TestFieldAccessOffOfThis() { @@ -1471,4 +1504,39 @@ void M(ref int i) { } } """, parseOptions: Preview); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26527")] + public async Task TestFixAllInDocument3() + { + await TestInRegularAndScript1Async( + """ + using System; + + public sealed class SomeViewModel + { + private bool {|FixAllInDocument:a|} = true; + public bool A { get => a; set => Set(ref a, value); } + + private bool b = true; + public bool B { get => b; set => Set(ref b, value); } + + private bool c = true; + public bool C { get => c; set => Set(ref c, value); } + + private void Set(ref T field, T value) => throw new NotImplementedException(); + } + """, + """ + using System; + + public sealed class SomeViewModel + { + public bool A { get; set => Set(ref field, value); } = true; + public bool B { get; set => Set(ref field, value); } = true; + public bool C { get; set => Set(ref field, value); } = true; + + private void Set(ref T field, T value) => throw new NotImplementedException(); + } + """, new TestParameters(parseOptions: Preview)); + } } diff --git a/src/Analyzers/CSharp/Tests/UseCoalesceExpression/UseCoalesceExpressionForIfNullStatementCheckTests.cs b/src/Analyzers/CSharp/Tests/UseCoalesceExpression/UseCoalesceExpressionForIfNullStatementCheckTests.cs index b90c9f295ab96..065c91b83751d 100644 --- a/src/Analyzers/CSharp/Tests/UseCoalesceExpression/UseCoalesceExpressionForIfNullStatementCheckTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCoalesceExpression/UseCoalesceExpressionForIfNullStatementCheckTests.cs @@ -202,7 +202,6 @@ void M(C item1) await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -226,7 +225,6 @@ void M() await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -250,7 +248,6 @@ void M() await new VerifyCS.Test { TestCode = text, - FixedCode = text, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -275,7 +272,6 @@ void M(C item1) await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -301,7 +297,6 @@ void M(C item1) await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -328,7 +323,6 @@ void M(C item1) await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -353,7 +347,6 @@ void M(C item1) await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -383,7 +376,6 @@ void M() await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -407,7 +399,6 @@ void M() await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -431,7 +422,6 @@ void M() await new VerifyCS.Test { TestCode = text, - FixedCode = text, }.RunAsync(); } @@ -455,7 +445,6 @@ void M() await new VerifyCS.Test { TestCode = text, - FixedCode = text, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs index 863c202e575e3..8e8bf8c71c873 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs @@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Analyzers.UnitTests.UseCollectionExpress CSharpUseCollectionExpressionForArrayCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionExpression)] -public class UseCollectionExpressionForArrayTests +public sealed class UseCollectionExpressionForArrayTests { [Fact] public async Task TestNotInCSharp11() diff --git a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForCreateTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForCreateTests.cs index 5c90dc6dd2fe0..14810cc0d08e3 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForCreateTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForCreateTests.cs @@ -489,7 +489,18 @@ public async Task TestCreateRange_ComputedExpression() class C { - MyCollection i = MyCollection.CreateRange(GetValues()); + MyCollection i = [|MyCollection.[|CreateRange|](|]GetValues()); + + static IEnumerable GetValues() => default; + } + """ + s_collectionBuilderApi + s_basicCollectionApi, + FixedCode = """ + using System; + using System.Collections.Generic; + + class C + { + MyCollection i = [.. GetValues()]; static IEnumerable GetValues() => default; } @@ -507,7 +518,13 @@ public async Task TestCreateRange_ExplicitArray1() TestCode = """ class C { - MyCollection i = MyCollection.CreateRange(new int [5]); + MyCollection i = [|MyCollection.[|CreateRange|](|]new int [5]); + } + """ + s_collectionBuilderApi + s_basicCollectionApi, + FixedCode = """ + class C + { + MyCollection i = [.. new int [5]]; } """ + s_collectionBuilderApi + s_basicCollectionApi, LanguageVersion = LanguageVersion.CSharp12, @@ -757,7 +774,15 @@ public async Task TestCreateRange_NewImplicitObject() class C { - MyCollection i = MyCollection.CreateRange({|CS0144:new() { }|]); + MyCollection i = [|MyCollection.[|CreateRange|](|]{|CS0144:new() { }|}); + } + """ + s_collectionBuilderApi + s_basicCollectionApi, + FixedCode = """ + using System.Collections.Generic; + + class C + { + MyCollection i = [.. {|CS8754:new() { }|}]; } """ + s_collectionBuilderApi + s_basicCollectionApi, LanguageVersion = LanguageVersion.CSharp12, @@ -1264,4 +1289,40 @@ class C """ }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75870")] + public async Task TestIEnumerablePassedToCreateRange() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + ImmutableArray GetFormattedRange() + { + return [|ImmutableArray.[|CreateRange|](|]Enumerable.Range(1, 10).Select(n => $"Item {n}")); + } + } + """, + FixedCode = """ + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + ImmutableArray GetFormattedRange() + { + return [.. Enumerable.Range(1, 10).Select(n => $"Item {n}")]; + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } } diff --git a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForFluentTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForFluentTests.cs index b3413c13b4dba..cc4097d2d84f3 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForFluentTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForFluentTests.cs @@ -2700,7 +2700,7 @@ void M() } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71145")] - public async Task TestNotInParallelEnumerable() + public async Task TestNotInParallelEnumerable1() { await new VerifyCS.Test { @@ -2711,6 +2711,55 @@ public async Task TestNotInParallelEnumerable() using System.Linq; using System.Linq.Expressions; + class C + { + void M() + { + const bool shouldParallelize = false; + + IEnumerable sequence = null!; + + var result = sequence.AsParallel().ToArray(); + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71145")] + public async Task TestNotInParallelEnumerable2() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + + class C + { + void M() + { + const bool shouldParallelize = false; + + IEnumerable sequence = null!; + + var result = shouldParallelize + ? sequence.AsParallel().ToArray() + : sequence.[|ToArray|](); + } + } + """, + FixedCode = + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + class C { void M() @@ -2721,7 +2770,7 @@ void M() var result = shouldParallelize ? sequence.AsParallel().ToArray() - : sequence.ToArray(); + : [.. sequence]; } } """, @@ -3066,4 +3115,40 @@ void M(int[] values, int[] x) ReferenceAssemblies = ReferenceAssemblies.Net.Net80, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75870")] + public async Task TestSelectToImmutableArray() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + ImmutableArray GetFormattedNumbers(ImmutableArray numbers) + { + return numbers.Select(n => $"Number: {n}").[|ToImmutableArray|](); + } + } + """, + FixedCode = """ + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + ImmutableArray GetFormattedNumbers(ImmutableArray numbers) + { + return [.. numbers.Select(n => $"Number: {n}")]; + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } } diff --git a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForNewTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForNewTests.cs new file mode 100644 index 0000000000000..a5fc8fb3683ca --- /dev/null +++ b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForNewTests.cs @@ -0,0 +1,97 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Testing; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.Analyzers.UnitTests.UseCollectionExpression; + +using VerifyCS = CSharpCodeFixVerifier< + CSharpUseCollectionExpressionForNewDiagnosticAnalyzer, + CSharpUseCollectionExpressionForNewCodeFixProvider>; + +[Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionExpression)] +public sealed class UseCollectionExpressionForNewTests +{ + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/75870")] + [InlineData("List")] + [InlineData("")] + public async Task TestIEnumerablePassedToListConstructor(string typeName) + { + await new VerifyCS.Test + { + TestCode = $$""" + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + List GetNumbers() + { + return [|[|new|] {{typeName}}(|]Enumerable.Range(1, 10)); + } + } + """, + FixedCode = """ + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + List GetNumbers() + { + return [.. Enumerable.Range(1, 10)]; + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/75870")] + [InlineData("List")] + [InlineData("")] + public async Task TestArrayPassedToListConstructor(string typeName) + { + await new VerifyCS.Test + { + TestCode = $$""" + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + List GetNumbers() + { + return [|[|new|] {{typeName}}(|]new[] { 1, 2, 3 }); + } + } + """, + FixedCode = """ + using System.Linq; + using System.Collections.Generic; + using System.Collections.Immutable; + + class C + { + List GetNumbers() + { + return [1, 2, 3]; + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } +} diff --git a/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests.cs index 5d9f3f4dcd37c..d70e33ad6cbad 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests.cs @@ -42,7 +42,6 @@ private static async Task TestMissingInRegularAndScriptAsync(string testCode, La var test = new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, }; if (languageVersion != null) diff --git a/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs b/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs index 7a19fa362e961..3d5ad523fa0a8 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs @@ -5866,4 +5866,26 @@ public class Class2 { } ReferenceAssemblies = ReferenceAssemblies.Net.Net80, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75894")] + public async Task TestNotOnDictionaryConstructor() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void Main() + { + Dictionary a = null; + Dictionary d = new(a); + } + } + """, + LanguageVersion = LanguageVersion.CSharp13, + ReferenceAssemblies = ReferenceAssemblies.Net.Net90, + }.RunAsync(); + } } diff --git a/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs b/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs index fb61260019e04..ea80f822af76d 100644 --- a/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs @@ -283,7 +283,6 @@ void M(int? a) await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_3 }.RunAsync(); } @@ -305,7 +304,6 @@ void M(int? a) await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp8 }.RunAsync(); } @@ -958,7 +956,6 @@ InsertionPoint Up() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, ReferenceAssemblies = ReferenceAssemblies.Net.Net60 }.RunAsync(); diff --git a/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundCoalesceAssignmentTests.cs b/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundCoalesceAssignmentTests.cs index eb005bef46ee7..ab6bafab2e87b 100644 --- a/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundCoalesceAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundCoalesceAssignmentTests.cs @@ -33,7 +33,6 @@ private static async Task TestMissingAsync(string testCode, LanguageVersion lang await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = languageVersion, ReferenceAssemblies = ReferenceAssemblies.Net.Net60, }.RunAsync(); diff --git a/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs b/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs index b390f823c05cb..03048bdecccd8 100644 --- a/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs @@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseConditionalExpressio CSharpUseConditionalExpressionForAssignmentCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsUseConditionalExpression)] -public partial class UseConditionalExpressionForAssignmentTests +public sealed partial class UseConditionalExpressionForAssignmentTests { private static async Task TestMissingAsync( string testCode, @@ -29,7 +29,6 @@ private static async Task TestMissingAsync( var test = new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = languageVersion, Options = { options }, }; @@ -2102,4 +2101,47 @@ public static void Test(object obj) } """); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71403")] + public async Task TestGlobalStatements() + { + await new VerifyCS.Test + { + TestCode = """ + #nullable enable + + using System; + + object? x = null; + object? y = null; + object? z; + + [|if|] (x != null) + { + z = x; + } + else + { + z = y; + } + + Console.WriteLine($"{x}{y}{z}"); + """, + FixedCode = """ + #nullable enable + + using System; + + object? x = null; + object? y = null; + object? z = x != null ? x : y; + + Console.WriteLine($"{x}{y}{z}"); + """, + LanguageVersion = LanguageVersion.CSharp9, + TestState = { + OutputKind = OutputKind.ConsoleApplication, + } + }.RunAsync(); + } } diff --git a/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForReturnTests.cs b/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForReturnTests.cs index fa03b73ac9ba4..667934aeeba06 100644 --- a/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForReturnTests.cs +++ b/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForReturnTests.cs @@ -16,16 +16,12 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseConditionalExpression; [Trait(Traits.Feature, Traits.Features.CodeActionsUseConditionalExpression)] -public partial class UseConditionalExpressionForReturnTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor +public sealed class UseConditionalExpressionForReturnTests(ITestOutputHelper logger) + : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor(logger) { private static readonly ParseOptions CSharp8 = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp8); private static readonly ParseOptions CSharp9 = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9); - public UseConditionalExpressionForReturnTests(ITestOutputHelper logger) - : base(logger) - { - } - internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (new CSharpUseConditionalExpressionForReturnDiagnosticAnalyzer(), new CSharpUseConditionalExpressionForReturnCodeFixProvider()); @@ -2281,4 +2277,66 @@ private bool AreSimilarCore(string node1, string node2) } """, title: AnalyzersResources.Simplify_check); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38879")] + public async Task TesSuppressionOperator() + { + await TestInRegularAndScript1Async(""" + #nullable enable + + class Program + { + public static string Method(bool empty) + { + [||]if (empty) + { + return string.Empty; + } + + return null!; + } + } + """, """ + #nullable enable + + class Program + { + public static string Method(bool empty) + { + return empty ? string.Empty : null!; + } + } + """); + } + + [Fact] + public async Task TestWithCollectionExpressions() + { + await TestInRegularAndScript1Async( + """ + class C + { + int[] M() + { + [||]if (true) + { + return [0]; + } + else + { + return [1]; + } + } + } + """, + """ + class C + { + int[] M() + { + return true ? [0] : [1]; + } + } + """); + } } diff --git a/src/Analyzers/CSharp/Tests/UseDefaultLiteral/UseDefaultLiteralTests.cs b/src/Analyzers/CSharp/Tests/UseDefaultLiteral/UseDefaultLiteralTests.cs index e7651ba47f906..b5e69a7715a2a 100644 --- a/src/Analyzers/CSharp/Tests/UseDefaultLiteral/UseDefaultLiteralTests.cs +++ b/src/Analyzers/CSharp/Tests/UseDefaultLiteral/UseDefaultLiteralTests.cs @@ -34,7 +34,6 @@ class C await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7 }.RunAsync(); } @@ -134,7 +133,6 @@ string Goo() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -188,7 +186,6 @@ void Goo() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -236,7 +233,6 @@ void Goo() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -257,7 +253,6 @@ void Goo() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -312,7 +307,6 @@ void Bar(int i) { } await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -482,7 +476,6 @@ public override bool Equals(object obj) await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -509,7 +502,6 @@ public override bool Equals(object obj) await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -566,7 +558,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -590,7 +581,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -647,7 +637,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -671,7 +660,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -758,7 +746,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } @@ -779,7 +766,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp7_1 }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/UseExplicitTupleName/UseExplicitTupleNameTests.cs b/src/Analyzers/CSharp/Tests/UseExplicitTupleName/UseExplicitTupleNameTests.cs index 7e24bf2ee9a9a..f173c92ce2cd2 100644 --- a/src/Analyzers/CSharp/Tests/UseExplicitTupleName/UseExplicitTupleNameTests.cs +++ b/src/Analyzers/CSharp/Tests/UseExplicitTupleName/UseExplicitTupleNameTests.cs @@ -220,7 +220,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.PreferExplicitTupleNames, false, NotificationOption2.Warning } @@ -245,7 +244,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, Options = { { CodeStyleOptions2.PreferExplicitTupleNames, false, NotificationOption2.Warning } @@ -298,7 +296,6 @@ void M() await new VerifyCS.Test() { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11 }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForAccessorsAnalyzerTests.cs b/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForAccessorsAnalyzerTests.cs index c496185cb85d1..de8c9a8781e08 100644 --- a/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForAccessorsAnalyzerTests.cs +++ b/src/Analyzers/CSharp/Tests/UseExpressionBody/UseExpressionBodyForAccessorsAnalyzerTests.cs @@ -575,7 +575,6 @@ C this[int index] await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ExpressionBodyPreference.WhenOnSingleLine, NotificationOption2.None }, diff --git a/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseIndexOperatorTests.cs b/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseIndexOperatorTests.cs index aaa1b12f1e9a8..22027513b98fb 100644 --- a/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseIndexOperatorTests.cs +++ b/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseIndexOperatorTests.cs @@ -38,7 +38,6 @@ void Goo(string s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7, }.RunAsync(); } @@ -243,7 +242,6 @@ void Goo(string s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -265,7 +263,6 @@ void Goo(string s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -288,7 +285,6 @@ void Goo(S s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -379,7 +375,6 @@ void Goo(S s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -402,7 +397,6 @@ void Goo(S s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -493,7 +487,6 @@ void Goo(S s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -516,7 +509,6 @@ void Goo(S s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -538,7 +530,6 @@ void Goo(string[] s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp20, TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -797,7 +788,6 @@ void Goo(Dictionary s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } @@ -822,7 +812,6 @@ void Goo(List s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } } diff --git a/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseRangeOperatorTests.cs b/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseRangeOperatorTests.cs index 1465302c90134..14447543578e5 100644 --- a/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseRangeOperatorTests.cs +++ b/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseRangeOperatorTests.cs @@ -38,7 +38,6 @@ void Goo(string s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7, }.RunAsync(); } @@ -98,7 +97,6 @@ void Goo(string s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp20, TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -399,7 +397,6 @@ void Goo(string s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp7, }.RunAsync(); } @@ -700,7 +697,6 @@ void Goo(S s) { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } [Fact] @@ -1311,7 +1307,6 @@ void M() { ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, TestCode = source, - FixedCode = source, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/UseNullPropagation/UseNullPropagationTests.cs b/src/Analyzers/CSharp/Tests/UseNullPropagation/UseNullPropagationTests.cs index fd23f28af5bf6..7b76c674dd345 100644 --- a/src/Analyzers/CSharp/Tests/UseNullPropagation/UseNullPropagationTests.cs +++ b/src/Analyzers/CSharp/Tests/UseNullPropagation/UseNullPropagationTests.cs @@ -43,7 +43,6 @@ private static async Task TestMissingInRegularAndScriptAsync(string testCode, La await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = languageVersion, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/UseObjectInitializer/UseObjectInitializerTests.cs b/src/Analyzers/CSharp/Tests/UseObjectInitializer/UseObjectInitializerTests.cs index 252322bd3d60e..9dbe9d05342a3 100644 --- a/src/Analyzers/CSharp/Tests/UseObjectInitializer/UseObjectInitializerTests.cs +++ b/src/Analyzers/CSharp/Tests/UseObjectInitializer/UseObjectInitializerTests.cs @@ -25,7 +25,6 @@ private static async Task TestMissingInRegularAndScriptAsync(string testCode, La var test = new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, }; if (languageVersion != null) diff --git a/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpAsAndMemberAccessTests.cs b/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpAsAndMemberAccessTests.cs index a8abf3c3d92cc..07fd91862e4ce 100644 --- a/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpAsAndMemberAccessTests.cs +++ b/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpAsAndMemberAccessTests.cs @@ -67,7 +67,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp7, }.RunAsync(); } @@ -89,7 +88,6 @@ void M(object o, int length) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp7, }.RunAsync(); } @@ -109,7 +107,6 @@ void M(object o, int length) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp7, }.RunAsync(); } @@ -134,7 +131,6 @@ void M(object o, int length) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp7, }.RunAsync(); } @@ -156,7 +152,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -178,7 +173,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -200,7 +194,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -222,7 +215,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -247,7 +239,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -271,7 +262,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -367,7 +357,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -391,7 +380,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -608,7 +596,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -630,7 +617,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -652,7 +638,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -677,7 +662,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -847,7 +831,6 @@ void M(object o) await new VerifyCS.Test { TestCode = test, - FixedCode = test, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } diff --git a/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpUseNotPatternTests.cs b/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpUseNotPatternTests.cs index 97c6ac0dd572c..98f97bbd10ddd 100644 --- a/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpUseNotPatternTests.cs +++ b/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpUseNotPatternTests.cs @@ -467,4 +467,44 @@ void M() LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/62078")] + public async Task TestTypeVersusMemberAmbiguity() + { + await new VerifyCS.Test + { + TestCode = """ + class Color { } + + class C + { + public const int Color = 0; + + void M(object x) + { + if (!(x [|is|] Color)) + { + } + } + } + """, + FixedCode = """ + class Color { } + + class C + { + public const int Color = 0; + + void M(object x) + { + if (x is not global::Color) + { + } + } + } + """, + LanguageVersion = LanguageVersion.CSharp9, + CodeActionValidationMode = Testing.CodeActionValidationMode.None, + }.RunAsync(); + } } diff --git a/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs b/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs index 283de4f8afb13..bcae949e0e205 100644 --- a/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs +++ b/src/Analyzers/CSharp/Tests/UseSimpleUsingStatement/UseSimpleUsingStatementTests.cs @@ -2,20 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.IO; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.UseSimpleUsingStatement; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; -using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseSimpleUsingStatement; @@ -24,7 +19,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseSimpleUsingStatement UseSimpleUsingStatementCodeFixProvider>; [Trait(Traits.Feature, Traits.Features.CodeActionsUseSimpleUsingStatement)] -public class UseSimpleUsingStatementTests +public sealed class UseSimpleUsingStatementTests { [Fact] public async Task TestAboveCSharp8() @@ -1920,4 +1915,209 @@ public static byte[] ComputeMD5Hash(byte[] source) } """); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75917")] + public async Task TestGlobalStatement1() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + [|using|] (var c = (IDisposable)null) + { + } + + class C + { + } + """, + FixedCode = """ + using System; + + using var c = (IDisposable)null; + + class C + { + } + """, + LanguageVersion = LanguageVersion.CSharp9, + TestState = + { + OutputKind = OutputKind.ConsoleApplication, + } + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75917")] + public async Task TestGlobalStatement2() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + [|using|] (var c = (IDisposable)null) + { + Console.WriteLine(); + } + + class C + { + } + """, + FixedCode = """ + using System; + + using var c = (IDisposable)null; + Console.WriteLine(); + + class C + { + } + """, + LanguageVersion = LanguageVersion.CSharp9, + TestState = + { + OutputKind = OutputKind.ConsoleApplication, + } + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75917")] + public async Task TestGlobalStatement3() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + using (var c = (IDisposable)null) + { + } + + Console.WriteLine(); + + class C + { + } + """, + LanguageVersion = LanguageVersion.CSharp9, + TestState = + { + OutputKind = OutputKind.ConsoleApplication, + } + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75917")] + public async Task TestGlobalStatement4() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + [|using|] (var c = (IDisposable)null) + { + Console.WriteLine(); + } + + int LocalFunction() => 0; + + class C + { + } + """, + FixedCode = """ + using System; + + using var c = (IDisposable)null; + Console.WriteLine(); + + int LocalFunction() => 0; + + class C + { + } + """, + LanguageVersion = LanguageVersion.CSharp9, + TestState = + { + OutputKind = OutputKind.ConsoleApplication, + } + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75917")] + public async Task TestGlobalStatement5() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + [|using|] (var c = (IDisposable)null) + using (var d = (IDisposable)null) + { + } + + class C + { + } + """, + FixedCode = """ + using System; + + using var c = (IDisposable)null; + using var d = (IDisposable)null; + + class C + { + } + """, + LanguageVersion = LanguageVersion.CSharp9, + TestState = + { + OutputKind = OutputKind.ConsoleApplication, + } + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75917")] + public async Task TestGlobalStatement6() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + [|using|] (var c = (IDisposable)null) + { + [|using|] (var d = (IDisposable)null) + { + } + } + + class C + { + } + """, + FixedCode = """ + using System; + + using var c = (IDisposable)null; + using var d = (IDisposable)null; + + class C + { + } + """, + LanguageVersion = LanguageVersion.CSharp9, + TestState = + { + OutputKind = OutputKind.ConsoleApplication, + } + }.RunAsync(); + } } diff --git a/src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs b/src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs index dd1ac5b1f3dc4..1a6209f36c637 100644 --- a/src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs +++ b/src/Analyzers/CSharp/Tests/UseTupleSwap/UseTupleSwapTests.cs @@ -18,8 +18,8 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseTupleSwap; CSharpUseTupleSwapDiagnosticAnalyzer, CSharpUseTupleSwapCodeFixProvider>; -[Trait(Traits.Feature, Traits.Features.CodeActionsUseLocalFunction)] -public partial class UseTupleSwapTests +[Trait(Traits.Feature, Traits.Features.CodeActionsUseTupleSwap)] +public sealed class UseTupleSwapTests { [Fact] public async Task TestMissingBeforeCSharp7() @@ -39,7 +39,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp6, }.RunAsync(); } @@ -62,7 +61,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, Options = { { CSharpCodeStyleOptions.PreferTupleSwap, false, CodeStyle.NotificationOption2.Silent } @@ -114,7 +112,6 @@ void M(ref int a, ref int b) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -216,7 +213,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -238,7 +234,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -260,7 +255,6 @@ void M(string[] args, string temp1) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -282,7 +276,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -304,7 +297,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -326,7 +318,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -348,7 +339,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -442,7 +432,6 @@ void M(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -495,7 +484,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -555,7 +543,6 @@ unsafe void M(int* v0, int* v1) await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } } diff --git a/src/Analyzers/CSharp/Tests/UseUnboundGenericTypeInNameOf/UseUnboundGenericTypeInNameOfTests.cs b/src/Analyzers/CSharp/Tests/UseUnboundGenericTypeInNameOf/UseUnboundGenericTypeInNameOfTests.cs new file mode 100644 index 0000000000000..4add90eb881f0 --- /dev/null +++ b/src/Analyzers/CSharp/Tests/UseUnboundGenericTypeInNameOf/UseUnboundGenericTypeInNameOfTests.cs @@ -0,0 +1,337 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; +using Microsoft.CodeAnalysis.CSharp.UseUnboundGenericTypeInNameOf; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseUnboundGenericTypeInNameOf; + +using VerifyCS = CSharpCodeFixVerifier< + CSharpUseUnboundGenericTypeInNameOfDiagnosticAnalyzer, + CSharpUseUnboundGenericTypeInNameOfCodeFixProvider>; + +[Trait(Traits.Feature, Traits.Features.CodeActionsUseUnboundGenericTypeInNameOf)] +public sealed class UseUnboundGenericTypeInNameOfTests +{ + [Fact] + public async Task TestBaseCase() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = [|nameof|](List); + } + } + """, + FixedCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(List<>); + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestNotIfAlreadyOmitted() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(List<>); + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestMissingBeforeCSharp14() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(List); + } + } + """, + LanguageVersion = LanguageVersion.CSharp13, + }.RunAsync(); + } + + [Fact] + public async Task TestMissingWithFeatureOff() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(List); + } + } + """, + Options = + { + { CSharpCodeStyleOptions.PreferUnboundGenericTypeInNameOf, false, CodeStyle.NotificationOption2.Silent } + }, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestMultipleTypeArguments() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = [|nameof|](Dictionary); + } + } + """, + FixedCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(Dictionary<,>); + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestGlobal() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = [|nameof|](global::System.Collections.Generic.Dictionary); + } + } + """, + FixedCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(global::System.Collections.Generic.Dictionary<,>); + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestNestedArgs() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = [|nameof|](Dictionary, string>); + } + } + """, + FixedCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(Dictionary<,>); + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestNestedType1() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = [|nameof|](Outer.Inner); + } + } + + class Outer { public class Inner { } } + """, + FixedCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(Outer<>.Inner<>); + } + } + + class Outer { public class Inner { } } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestNestedType2() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = [|nameof|](Outer.Inner<>); + } + } + + class Outer { public class Inner { } } + """, + FixedCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(Outer<>.Inner<>); + } + } + + class Outer { public class Inner { } } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestNestedType3() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = [|nameof|](Outer<>.Inner); + } + } + + class Outer { public class Inner { } } + """, + FixedCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(Outer<>.Inner<>); + } + } + + class Outer { public class Inner { } } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestNestedType4() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + class C + { + void M(string[] args) + { + var v = nameof(Outer<>.Inner<>); + } + } + + class Outer { public class Inner { } } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } +} diff --git a/src/Analyzers/Core/Analyzers/Analyzers.projitems b/src/Analyzers/Core/Analyzers/Analyzers.projitems index ea81bd2ba5653..45e0c4005a393 100644 --- a/src/Analyzers/Core/Analyzers/Analyzers.projitems +++ b/src/Analyzers/Core/Analyzers/Analyzers.projitems @@ -10,7 +10,9 @@ - + + Designer + diff --git a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs index ec58d8982660a..832acfb42498e 100644 --- a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs +++ b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs @@ -96,8 +96,10 @@ internal static class EnforceOnBuildValues public const EnforceOnBuild UseCollectionExpressionForCreate = /*IDE0303*/ EnforceOnBuild.Recommended; public const EnforceOnBuild UseCollectionExpressionForBuilder = /*IDE0304*/ EnforceOnBuild.Recommended; public const EnforceOnBuild UseCollectionExpressionForFluent = /*IDE0305*/ EnforceOnBuild.Recommended; + public const EnforceOnBuild UseCollectionExpressionForNew = /*IDE0306*/ EnforceOnBuild.Recommended; public const EnforceOnBuild MakeAnonymousFunctionStatic = /*IDE0320*/ EnforceOnBuild.Recommended; public const EnforceOnBuild UseSystemThreadingLock = /*IDE0330*/ EnforceOnBuild.Recommended; + public const EnforceOnBuild UseUnboundGenericTypeInNameOf = /*IDE0340*/ EnforceOnBuild.Recommended; /* EnforceOnBuild.WhenExplicitlyEnabled */ public const EnforceOnBuild RemoveUnnecessaryCast = /*IDE0004*/ EnforceOnBuild.WhenExplicitlyEnabled; // TODO: Move to 'Recommended' OR 'HighlyRecommended' bucket once performance problems are addressed: https://github.com/dotnet/roslyn/issues/43304 diff --git a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs index 91856fd7d7468..e4b9e037db772 100644 --- a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs +++ b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs @@ -198,11 +198,14 @@ internal static class IDEDiagnosticIds public const string UseCollectionExpressionForCreateDiagnosticId = "IDE0303"; public const string UseCollectionExpressionForBuilderDiagnosticId = "IDE0304"; public const string UseCollectionExpressionForFluentDiagnosticId = "IDE0305"; + public const string UseCollectionExpressionForNewDiagnosticId = "IDE0306"; public const string MakeAnonymousFunctionStaticDiagnosticId = "IDE0320"; public const string UseSystemThreadingLockDiagnosticId = "IDE0330"; + public const string UseUnboundGenericTypeInNameOfDiagnosticId = "IDE0340"; + // Analyzer error Ids public const string AnalyzerChangedId = "IDE1001"; public const string AnalyzerDependencyConflictId = "IDE1002"; diff --git a/src/Analyzers/Core/Analyzers/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs index 6c860d117931f..72325a9dc6379 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs @@ -71,8 +71,6 @@ private static ImmutableArray GetDescriptors(LocalizableSt protected abstract bool IsRegularCommentOrDocComment(SyntaxTrivia trivia); protected abstract IUnnecessaryImportsProvider UnnecessaryImportsProvider { get; } - protected override GeneratedCodeAnalysisFlags GeneratedCodeAnalysisFlags => GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics; - protected abstract SyntaxToken? TryGetLastToken(SyntaxNode node); protected override void InitializeWorker(AnalysisContext context) diff --git a/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs index 814efb24c087c..0e719110d813d 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.CodeQuality; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -25,8 +26,11 @@ internal abstract class AbstractRemoveUnusedMembersDiagnosticAnalyzer< TDocumentationCommentTriviaSyntax, TIdentifierNameSyntax, TTypeDeclarationSyntax, - TMemberDeclarationSyntax> - : AbstractCodeQualityDiagnosticAnalyzer + TMemberDeclarationSyntax>() + : AbstractCodeQualityDiagnosticAnalyzer( + [s_removeUnusedMembersRule, s_removeUnreadMembersRule], + // We want to analyze references in generated code, but not report unused members in generated code. + GeneratedCodeAnalysisFlags.Analyze) where TDocumentationCommentTriviaSyntax : SyntaxNode where TIdentifierNameSyntax : SyntaxNode where TTypeDeclarationSyntax : TMemberDeclarationSyntax @@ -56,11 +60,7 @@ internal abstract class AbstractRemoveUnusedMembersDiagnosticAnalyzer< new LocalizableResourceString(nameof(AnalyzersResources.Private_member_0_can_be_removed_as_the_value_assigned_to_it_is_never_read), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)), hasAnyCodeStyleOption: false, isUnnecessary: true); - protected AbstractRemoveUnusedMembersDiagnosticAnalyzer() - : base([s_removeUnusedMembersRule, s_removeUnreadMembersRule], - GeneratedCodeAnalysisFlags.Analyze) // We want to analyze references in generated code, but not report unused members in generated code. - { - } + protected abstract ISemanticFacts SemanticFacts { get; } protected abstract IEnumerable GetTypeDeclarations(INamedTypeSymbol namedType, CancellationToken cancellationToken); protected abstract SyntaxList GetMembers(TTypeDeclarationSyntax typeDeclaration); @@ -86,7 +86,8 @@ protected virtual void HandleNamedTypeSymbolStart(SymbolStartAnalysisContext con private sealed class CompilationAnalyzer { - private readonly object _gate; + private readonly object _gate = new(); + /// /// State map for candidate member symbols, with the value indicating how each symbol is used in executable code. /// @@ -114,7 +115,6 @@ private CompilationAnalyzer( Compilation compilation, AbstractRemoveUnusedMembersDiagnosticAnalyzer analyzer) { - _gate = new object(); _analyzer = analyzer; _taskType = compilation.TaskType(); @@ -198,21 +198,25 @@ private void RegisterActions(CompilationStartAnalysisContext compilationStartCon if (!ShouldAnalyze(symbolStartContext, (INamedTypeSymbol)symbolStartContext.Symbol)) return; - var hasUnsupportedOperation = false; - symbolStartContext.RegisterOperationAction(AnalyzeMemberReferenceOperation, OperationKind.FieldReference, OperationKind.MethodReference, OperationKind.PropertyReference, OperationKind.EventReference); + symbolStartContext.RegisterOperationAction(AnalyzeDeconstructionAssignment, OperationKind.DeconstructionAssignment); symbolStartContext.RegisterOperationAction(AnalyzeFieldInitializer, OperationKind.FieldInitializer); symbolStartContext.RegisterOperationAction(AnalyzeInvocationOperation, OperationKind.Invocation); + symbolStartContext.RegisterOperationAction(AnalyzeLoopOperation, OperationKind.Loop); + symbolStartContext.RegisterOperationAction(AnalyzeMemberReferenceOperation, OperationKind.FieldReference, OperationKind.MethodReference, OperationKind.PropertyReference, OperationKind.EventReference); symbolStartContext.RegisterOperationAction(AnalyzeNameOfOperation, OperationKind.NameOf); symbolStartContext.RegisterOperationAction(AnalyzeObjectCreationOperation, OperationKind.ObjectCreation); // We bail out reporting diagnostics for named types if it contains following kind of operations: - // 1. Invalid operations, i.e. erroneous code: - // We do so to ensure that we don't report false positives during editing scenarios in the IDE, where the user - // is still editing code and fixing unresolved references to symbols, such as overload resolution errors. + // 1. Invalid operations, i.e. erroneous code: We do so to ensure that we don't report false positives + // during editing scenarios in the IDE, where the user is still editing code and fixing unresolved + // references to symbols, such as overload resolution errors. // 2. Dynamic operations, where we do not know the exact member being referenced at compile time. // 3. Operations with OperationKind.None. - symbolStartContext.RegisterOperationAction(_ => hasUnsupportedOperation = true, OperationKind.Invalid, OperationKind.None, - OperationKind.DynamicIndexerAccess, OperationKind.DynamicInvocation, OperationKind.DynamicMemberReference, OperationKind.DynamicObjectCreation); + + var hasUnsupportedOperation = false; + symbolStartContext.RegisterOperationAction( + _ => hasUnsupportedOperation = true, + OperationKind.Invalid, OperationKind.None, OperationKind.DynamicIndexerAccess, OperationKind.DynamicInvocation, OperationKind.DynamicMemberReference, OperationKind.DynamicObjectCreation); symbolStartContext.RegisterSymbolEndAction(symbolEndContext => OnSymbolEnd(symbolEndContext, hasUnsupportedOperation)); @@ -253,14 +257,19 @@ private void AnalyzeSymbolDeclaration(SymbolAnalysisContext symbolContext) // Note that we might receive a symbol reference (AnalyzeMemberOperation) callback before // this symbol declaration callback, so even though we cannot receive duplicate callbacks for a symbol, // an entry might already be present of the declared symbol here. - if (!_symbolValueUsageStateMap.ContainsKey(symbol)) - { - _symbolValueUsageStateMap.Add(symbol, ValueUsageInfo.None); - } + _symbolValueUsageStateMap.TryAdd(symbol, ValueUsageInfo.None); } } } + private void AnalyzeDeconstructionAssignment(OperationAnalysisContext operationContext) + { + var operation = operationContext.Operation; + var methods = _analyzer.SemanticFacts.GetDeconstructionAssignmentMethods(operation.SemanticModel!, operation.Syntax); + foreach (var method in methods) + OnSymbolUsage(method, ValueUsageInfo.Read); + } + private void AnalyzeFieldInitializer(OperationAnalysisContext operationContext) { // Check if the initialized fields are being initialized a non-constant value. @@ -369,6 +378,18 @@ memberReference.Parent is IIncrementOrDecrementOperation || } } + private void AnalyzeLoopOperation(OperationAnalysisContext operationContext) + { + var operation = operationContext.Operation; + if (operation is not IForEachLoopOperation loopOperation) + return; + + var symbols = _analyzer.SemanticFacts.GetForEachSymbols(operation.SemanticModel!, loopOperation.Syntax); + OnSymbolUsage(symbols.CurrentProperty, ValueUsageInfo.Read); + OnSymbolUsage(symbols.GetEnumeratorMethod, ValueUsageInfo.Read); + OnSymbolUsage(symbols.MoveNextMethod, ValueUsageInfo.Read); + } + private void AnalyzeInvocationOperation(OperationAnalysisContext operationContext) { var targetMethod = ((IInvocationOperation)operationContext.Operation).TargetMethod.OriginalDefinition; @@ -380,9 +401,7 @@ private void AnalyzeInvocationOperation(OperationAnalysisContext operationContex // If the invoked method is a reduced extension method, also mark the original // method from which it was reduced as "used". if (targetMethod.ReducedFrom != null) - { OnSymbolUsage(targetMethod.ReducedFrom, ValueUsageInfo.Read); - } } private void AnalyzeNameOfOperation(OperationAnalysisContext operationContext) diff --git a/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs index 6b84494d3acfc..671b11f76ec94 100644 --- a/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs @@ -80,7 +80,13 @@ protected AbstractUseCollectionInitializerDiagnosticAnalyzer() protected abstract bool AreCollectionInitializersSupported(Compilation compilation); protected abstract bool AreCollectionExpressionsSupported(Compilation compilation); protected abstract bool CanUseCollectionExpression( - SemanticModel semanticModel, TObjectCreationExpressionSyntax objectCreationExpression, INamedTypeSymbol? expressionType, bool allowSemanticsChange, CancellationToken cancellationToken, out bool changesSemantics); + SemanticModel semanticModel, + TObjectCreationExpressionSyntax objectCreationExpression, + INamedTypeSymbol? expressionType, + ImmutableArray> preMatches, + bool allowSemanticsChange, + CancellationToken cancellationToken, + out bool changesSemantics); protected abstract TAnalyzer GetAnalyzer(); @@ -218,18 +224,18 @@ private void AnalyzeNode( if (!this.AreCollectionExpressionsSupported(context.Compilation)) return null; - var (_, matches) = analyzer.Analyze(semanticModel, syntaxFacts, objectCreationExpression, analyzeForCollectionExpression: true, cancellationToken); + var (preMatches, postMatches) = analyzer.Analyze(semanticModel, syntaxFacts, objectCreationExpression, analyzeForCollectionExpression: true, cancellationToken); // If analysis failed, we can't change this, no matter what. - if (matches.IsDefault) + if (preMatches.IsDefault || postMatches.IsDefault) return null; // Check if it would actually be legal to use a collection expression here though. var allowSemanticsChange = preferExpressionOption.Value == CollectionExpressionPreference.WhenTypesLooselyMatch; - if (!CanUseCollectionExpression(semanticModel, objectCreationExpression, expressionType, allowSemanticsChange, cancellationToken, out var changesSemantics)) + if (!CanUseCollectionExpression(semanticModel, objectCreationExpression, expressionType, preMatches, allowSemanticsChange, cancellationToken, out var changesSemantics)) return null; - return (matches, shouldUseCollectionExpression: true, changesSemantics); + return (preMatches.Concat(postMatches), shouldUseCollectionExpression: true, changesSemantics); } } diff --git a/src/Analyzers/Core/CodeFixes/CodeFixes.projitems b/src/Analyzers/Core/CodeFixes/CodeFixes.projitems index 4bd44bf11c75d..a2c57739a682d 100644 --- a/src/Analyzers/Core/CodeFixes/CodeFixes.projitems +++ b/src/Analyzers/Core/CodeFixes/CodeFixes.projitems @@ -129,7 +129,7 @@ - + diff --git a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs index e6e5e1a492e32..df67659b2018e 100644 --- a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs +++ b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs @@ -137,6 +137,7 @@ internal static class PredefinedCodeFixProviderNames public const string UseCollectionExpressionForCreate = nameof(UseCollectionExpressionForCreate); public const string UseCollectionExpressionForEmpty = nameof(UseCollectionExpressionForEmpty); public const string UseCollectionExpressionForFluent = nameof(UseCollectionExpressionForFluent); + public const string UseCollectionExpressionForNew = nameof(UseCollectionExpressionForNew); public const string UseCollectionExpressionForStackAlloc = nameof(UseCollectionExpressionForStackAlloc); public const string UseCollectionInitializer = nameof(UseCollectionInitializer); public const string UseCompoundAssignment = nameof(UseCompoundAssignment); @@ -177,5 +178,6 @@ internal static class PredefinedCodeFixProviderNames public const string UseSystemThreadingLock = nameof(UseSystemThreadingLock); public const string UseThrowExpression = nameof(UseThrowExpression); public const string UseTupleSwap = nameof(UseTupleSwap); + public const string UseUnboundGenericTypeInNameOf = nameof(UseUnboundGenericTypeInNameOf); public const string UseUtf8StringLiteral = nameof(UseUtf8StringLiteral); } diff --git a/src/Analyzers/Core/CodeFixes/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsCodeFixProvider.cs index a731d282a912c..c12b81448274a 100644 --- a/src/Analyzers/Core/CodeFixes/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsCodeFixProvider.cs @@ -2,13 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -30,7 +28,7 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) context.RegisterCodeFix( CodeAction.Create( title, - c => RemoveUnnecessaryImportsAsync(context.Document, c), + cancellationToken => RemoveUnnecessaryImportsAsync(context.Document, cancellationToken), title), context.Diagnostics); return Task.CompletedTask; @@ -38,11 +36,11 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) protected abstract string GetTitle(); - private static async Task RemoveUnnecessaryImportsAsync( + private static Task RemoveUnnecessaryImportsAsync( Document document, CancellationToken cancellationToken) { var service = document.GetRequiredLanguageService(); - return await service.RemoveUnnecessaryImportsAsync(document, cancellationToken).ConfigureAwait(false); + return service.RemoveUnnecessaryImportsAsync(document, cancellationToken); } } diff --git a/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider`3.cs b/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider`3.cs deleted file mode 100644 index fd404a25cbadf..0000000000000 --- a/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/AbstractSimplifyLinqExpressionCodeFixProvider`3.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.LanguageService; - -namespace Microsoft.CodeAnalysis.SimplifyLinqExpression; - -internal abstract class AbstractSimplifyLinqExpressionCodeFixProvider : SyntaxEditorBasedCodeFixProvider - where TExpressionSyntax : SyntaxNode - where TInvocationExpressionSyntax : TExpressionSyntax - where TSimpleNameSyntax : TExpressionSyntax -{ - protected abstract ISyntaxFacts SyntaxFacts { get; } - - public sealed override ImmutableArray FixableDiagnosticIds - => [IDEDiagnosticIds.SimplifyLinqExpressionDiagnosticId]; - - public override Task RegisterCodeFixesAsync(CodeFixContext context) - { - RegisterCodeFix(context, AnalyzersResources.Simplify_LINQ_expression, nameof(AnalyzersResources.Simplify_LINQ_expression)); - return Task.CompletedTask; - } - - protected override Task FixAllAsync(Document document, - ImmutableArray diagnostics, - SyntaxEditor editor, - CancellationToken cancellationToken) - { - var root = editor.OriginalRoot; - var expressionsToReWrite = diagnostics.Select(d => GetInvocation(root, d)).OrderByDescending(i => i.SpanStart); - foreach (var original in expressionsToReWrite) - { - editor.ReplaceNode(original, (current, generator) => - { - var invocation = (TInvocationExpressionSyntax)current; - var (expression, name, arguments) = FindNodes(invocation); - return generator.InvocationExpression( - generator.MemberAccessExpression(expression, name), - arguments); - }); - } - - return Task.CompletedTask; - - static TInvocationExpressionSyntax GetInvocation(SyntaxNode root, Diagnostic diagnostic) - { - return (TInvocationExpressionSyntax)root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); - } - - (TExpressionSyntax Expression, TSimpleNameSyntax Name, SeparatedSyntaxList Arguments) FindNodes(TInvocationExpressionSyntax current) - { - var memberAccess = SyntaxFacts.GetExpressionOfInvocationExpression(current); - var name = (TSimpleNameSyntax)SyntaxFacts.GetNameOfMemberAccessExpression(memberAccess); - var whereExpression = (TInvocationExpressionSyntax)SyntaxFacts.GetExpressionOfMemberAccessExpression(memberAccess)!; - var arguments = SyntaxFacts.GetArgumentsOfInvocationExpression(whereExpression); - var expression = (TExpressionSyntax)SyntaxFacts.GetExpressionOfMemberAccessExpression(SyntaxFacts.GetExpressionOfInvocationExpression(whereExpression))!; - return (expression, name, arguments); - } - } -} diff --git a/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/SimplifyLinqExpressionCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/SimplifyLinqExpressionCodeFixProvider.cs new file mode 100644 index 0000000000000..d75e75c4921c7 --- /dev/null +++ b/src/Analyzers/Core/CodeFixes/SimplifyLinqExpression/SimplifyLinqExpressionCodeFixProvider.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Composition; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.SimplifyLinqExpression; + +[ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = PredefinedCodeFixProviderNames.SimplifyLinqExpression), Shared] +[method: ImportingConstructor] +[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] +internal sealed class SimplifyLinqExpressionCodeFixProvider() : SyntaxEditorBasedCodeFixProvider +{ + public sealed override ImmutableArray FixableDiagnosticIds + => [IDEDiagnosticIds.SimplifyLinqExpressionDiagnosticId]; + + public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) + { + RegisterCodeFix(context, AnalyzersResources.Simplify_LINQ_expression, nameof(AnalyzersResources.Simplify_LINQ_expression)); + return Task.CompletedTask; + } + + protected override Task FixAllAsync( + Document document, + ImmutableArray diagnostics, + SyntaxEditor editor, + CancellationToken cancellationToken) + { + var syntaxFacts = document.GetRequiredLanguageService(); + var root = editor.OriginalRoot; + + foreach (var diagnostic in diagnostics.OrderByDescending(diagnostics => diagnostics.Location.SourceSpan.Start)) + { + var invocation = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + + editor.ReplaceNode(invocation, (current, generator) => + { + // 'current' is the original full expression. like x.Where(...).Count(); + + // 'x.Where(...).Count' in the above expression + var memberAccess = syntaxFacts.GetExpressionOfInvocationExpression(current); + + // 'Count' in the above expression + var outerName = syntaxFacts.GetNameOfMemberAccessExpression(memberAccess); + + // 'x.Where(...)' in the above expression. + var innerInvocationExpression = syntaxFacts.GetExpressionOfMemberAccessExpression(memberAccess)!; + + // 'x.Where' in the above expression. + var innerMemberAccessExpression = syntaxFacts.GetExpressionOfInvocationExpression(innerInvocationExpression); + + // 'Where' in the above expression. + var innerName = syntaxFacts.GetNameOfMemberAccessExpression(innerMemberAccessExpression); + + // trim down to the 'x.Where(...)', except with 'Where' replaced with 'Count'. + return innerInvocationExpression.ReplaceNode(innerName, outerName.WithTriviaFrom(innerName)).WithTrailingTrivia(current.GetTrailingTrivia()); + }); + } + + return Task.CompletedTask; + } +} diff --git a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs index fcba559fe08f2..30b983f726ef0 100644 --- a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; @@ -161,7 +160,12 @@ private TExpressionSyntax CastValueIfNecessary( if (statement is IThrowOperation throwOperation) return ConvertToExpression(throwOperation); - var sourceSyntax = value.Syntax.WithoutTrivia(); + var suppressKind = this.SyntaxFacts.SyntaxKinds.SuppressNullableWarningExpression; + var sourceSyntax = value.Syntax; + while (sourceSyntax is { Parent.RawKind: var kind } && kind == suppressKind) + sourceSyntax = sourceSyntax.Parent; + + sourceSyntax = sourceSyntax.WithoutTrivia(); // If there was an implicit conversion generated by the compiler, then convert that to an // explicit conversion inside the condition. This is needed as there is no type diff --git a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/ForAssignment/AbstractUseConditionalExpressionForAssignmentCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/ForAssignment/AbstractUseConditionalExpressionForAssignmentCodeFixProvider.cs index 0c096ac320694..b26a1934237d3 100644 --- a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/ForAssignment/AbstractUseConditionalExpressionForAssignmentCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/ForAssignment/AbstractUseConditionalExpressionForAssignmentCodeFixProvider.cs @@ -60,7 +60,7 @@ protected override async Task FixOneAsync( SyntaxEditor editor, SyntaxFormattingOptions formattingOptions, CancellationToken cancellationToken) { var syntaxFacts = document.GetRequiredLanguageService(); - var ifStatement = diagnostic.AdditionalLocations[0].FindNode(cancellationToken); + var ifStatement = diagnostic.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var ifOperation = (IConditionalOperation)semanticModel.GetOperation(ifStatement, cancellationToken)!; diff --git a/src/Analyzers/VisualBasic/Analyzers/RemoveUnusedMembers/VisualBasicRemoveUnusedMembersDiagnosticAnalyzer.vb b/src/Analyzers/VisualBasic/Analyzers/RemoveUnusedMembers/VisualBasicRemoveUnusedMembersDiagnosticAnalyzer.vb index 21a7da1fd0061..fdb5b329bf038 100644 --- a/src/Analyzers/VisualBasic/Analyzers/RemoveUnusedMembers/VisualBasicRemoveUnusedMembersDiagnosticAnalyzer.vb +++ b/src/Analyzers/VisualBasic/Analyzers/RemoveUnusedMembers/VisualBasicRemoveUnusedMembersDiagnosticAnalyzer.vb @@ -4,6 +4,7 @@ Imports System.Threading Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.LanguageService Imports Microsoft.CodeAnalysis.RemoveUnusedMembers Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -16,6 +17,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnusedMembers TypeBlockSyntax, StatementSyntax) + Protected Overrides ReadOnly Property SemanticFacts As ISemanticFacts = VisualBasicSemanticFacts.Instance + Protected Overrides Sub HandleNamedTypeSymbolStart(context As SymbolStartAnalysisContext, onSymbolUsageFound As Action(Of ISymbol, ValueUsageInfo)) ' Mark all methods with handles clause as having a read reference ' to ensure that we consider the method as "used". diff --git a/src/Analyzers/VisualBasic/Analyzers/UseCollectionInitializer/VisualBasicUseCollectionInitializerDiagnosticAnalyzer.vb b/src/Analyzers/VisualBasic/Analyzers/UseCollectionInitializer/VisualBasicUseCollectionInitializerDiagnosticAnalyzer.vb index b15c4bf2bd948..f8227d27903ee 100644 --- a/src/Analyzers/VisualBasic/Analyzers/UseCollectionInitializer/VisualBasicUseCollectionInitializerDiagnosticAnalyzer.vb +++ b/src/Analyzers/VisualBasic/Analyzers/UseCollectionInitializer/VisualBasicUseCollectionInitializerDiagnosticAnalyzer.vb @@ -2,9 +2,11 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. +Imports System.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.LanguageService +Imports Microsoft.CodeAnalysis.UseCollectionExpression Imports Microsoft.CodeAnalysis.UseCollectionInitializer Imports Microsoft.CodeAnalysis.VisualBasic.LanguageService Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -38,7 +40,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseCollectionInitializer Return False End Function - Protected Overrides Function CanUseCollectionExpression(semanticModel As SemanticModel, objectCreationExpression As ObjectCreationExpressionSyntax, expressionType As INamedTypeSymbol, allowSemanticsChange As Boolean, cancellationToken As CancellationToken, ByRef changesSemantics As Boolean) As Boolean + Protected Overrides Function CanUseCollectionExpression( + semanticModel As SemanticModel, + objectCreationExpression As ObjectCreationExpressionSyntax, + expressionType As INamedTypeSymbol, + matches As ImmutableArray(Of CollectionMatch(Of SyntaxNode)), + allowSemanticsChange As Boolean, + cancellationToken As CancellationToken, + ByRef changesSemantics As Boolean) As Boolean Throw ExceptionUtilities.Unreachable() End Function End Class diff --git a/src/Analyzers/VisualBasic/CodeFixes/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionCodeFixProvider.vb b/src/Analyzers/VisualBasic/CodeFixes/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionCodeFixProvider.vb deleted file mode 100644 index b2317a224cf4f..0000000000000 --- a/src/Analyzers/VisualBasic/CodeFixes/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionCodeFixProvider.vb +++ /dev/null @@ -1,26 +0,0 @@ -' Licensed to the .NET Foundation under one or more agreements. -' The .NET Foundation licenses this file to you under the MIT license. -' See the LICENSE file in the project root for more information. - -Imports System.Composition -Imports System.Diagnostics.CodeAnalysis -Imports Microsoft.CodeAnalysis.CodeFixes -Imports Microsoft.CodeAnalysis.LanguageService -Imports Microsoft.CodeAnalysis.SimplifyLinqExpression -Imports Microsoft.CodeAnalysis.VisualBasic.LanguageService -Imports Microsoft.CodeAnalysis.VisualBasic.Syntax - -Namespace Microsoft.CodeAnalysis.VisualBasic.SimplifyLinqExpression - - Friend Class VisualBasicSimplifyLinqExpressionCodeFixProvider - Inherits AbstractSimplifyLinqExpressionCodeFixProvider(Of - InvocationExpressionSyntax, SimpleNameSyntax, ExpressionSyntax) - - - - Public Sub New() - End Sub - - Protected Overrides ReadOnly Property SyntaxFacts As ISyntaxFacts = VisualBasicSyntaxFacts.Instance - End Class -End Namespace diff --git a/src/Analyzers/VisualBasic/CodeFixes/VisualBasicCodeFixes.projitems b/src/Analyzers/VisualBasic/CodeFixes/VisualBasicCodeFixes.projitems index a25a0e527ed83..dcdf21dec4791 100644 --- a/src/Analyzers/VisualBasic/CodeFixes/VisualBasicCodeFixes.projitems +++ b/src/Analyzers/VisualBasic/CodeFixes/VisualBasicCodeFixes.projitems @@ -63,7 +63,6 @@ - diff --git a/src/Analyzers/VisualBasic/Tests/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionFixAllTests.vb b/src/Analyzers/VisualBasic/Tests/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionFixAllTests.vb index 4288e2004cb24..ca5f9856e7799 100644 --- a/src/Analyzers/VisualBasic/Tests/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionFixAllTests.vb +++ b/src/Analyzers/VisualBasic/Tests/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionFixAllTests.vb @@ -3,6 +3,7 @@ ' See the LICENSE file in the project root for more information. Imports Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions +Imports Microsoft.CodeAnalysis.SimplifyLinqExpression Namespace Microsoft.CodeAnalysis.VisualBasic.SimplifyLinqExpression @@ -41,7 +42,7 @@ Module T Dim test5 = test.FirstOrDefault(Function(x) x.Equals(""!"")) End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) End Function @@ -78,7 +79,7 @@ Module T Dim test5 = Enumerable.FirstOrDefault(test, Function(x) x.Equals(""!"")) End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) End Function @@ -115,7 +116,7 @@ Module T Dim test5 = test.FirstOrDefault(Function(x) x.FirstOrDefault(Function(s) s.Equals(""!"")).Equals(""!"")) End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) End Function End Class End Namespace diff --git a/src/Analyzers/VisualBasic/Tests/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionTests.vb b/src/Analyzers/VisualBasic/Tests/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionTests.vb index 816e9b43d9e25..c4fc3b266cf4a 100644 --- a/src/Analyzers/VisualBasic/Tests/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionTests.vb +++ b/src/Analyzers/VisualBasic/Tests/SimplifyLinqExpression/VisualBasicSimplifyLinqExpressionTests.vb @@ -3,6 +3,7 @@ ' See the LICENSE file in the project root for more information. Imports Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions +Imports Microsoft.CodeAnalysis.SimplifyLinqExpression Namespace Microsoft.CodeAnalysis.VisualBasic.SimplifyLinqExpression @@ -40,7 +41,7 @@ Module T End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) End Function @@ -65,7 +66,7 @@ Module T End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) End Function @@ -100,7 +101,7 @@ Module T Dim test = (From x In data).{methodName}(Function(x) x = 1) End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) End Function @@ -125,7 +126,7 @@ Module T End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) End Function @@ -166,7 +167,7 @@ Module T End Function) End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) End Function @@ -191,7 +192,7 @@ Module T Dim output = testvar2.Where(Function(x) x = 4).{methodName}() End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) End Function @@ -236,7 +237,7 @@ Module T Dim test1 = test.{firstMethod}(Function(x) x.{secondMethod}(Function(c) c.Equals(""!"")).Equals(""!"")) End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) End Function @@ -272,7 +273,7 @@ Module T End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyCodeFixAsync(testCode, fixedCode) End Function @@ -296,7 +297,7 @@ Module T Dim output = testvar1.Where(Function(x) x = 4).{methodName}(Function(x) x <> 1) End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) End Function @@ -312,7 +313,7 @@ Module T Dim output = testvar1.Where(Function(x) x = 4).Count() End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) End Function @@ -341,7 +342,7 @@ Module T Dim result = queryableData.Where(Expression.Lambda(Of Func(Of String, Boolean))(predicateBody, pe)).First() End Sub End Module" - Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, VisualBasicSimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) + Await VisualBasicCodeFixVerifier(Of VisualBasicSimplifyLinqExpressionDiagnosticAnalyzer, SimplifyLinqExpressionCodeFixProvider).VerifyAnalyzerAsync(testCode) End Function End Class End Namespace diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index aea6580634b3a..a69dfe3c04d33 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -16,10 +16,10 @@ namespace Microsoft.CodeAnalysis.CSharp { internal partial class RefSafetyAnalysis { - private enum EscapeLevel : uint + private enum EscapeLevel { - CallingMethod = CallingMethodScope, - ReturnOnly = ReturnOnlyScope, + CallingMethod, + ReturnOnly } /// @@ -216,29 +216,6 @@ public void Deconstruct(out ParameterSymbol? parameter, out BoundExpression argu ? p.ToString() : Argument.ToString(); } - - /// - /// For the purpose of escape verification we operate with the depth of local scopes. - /// The depth is a uint, with smaller number representing shallower/wider scopes. - /// 0, 1 and 2 are special scopes - - /// 0 is the "calling method" scope that is outside of the containing method/lambda. - /// If something can escape to scope 0, it can escape to any scope in a given method through a ref parameter or return. - /// 1 is the "return-only" scope that is outside of the containing method/lambda. - /// If something can escape to scope 1, it can escape to any scope in a given method or can be returned, but it can't escape through a ref parameter. - /// 2 is the "current method" scope that is just inside the containing method/lambda. - /// If something can escape to scope 1, it can escape to any scope in a given method, but cannot be returned. - /// n + 1 corresponds to scopes immediately inside a scope of depth n. - /// Since sibling scopes do not intersect and a value cannot escape from one to another without - /// escaping to a wider scope, we can use simple depth numbering without ambiguity. - /// - /// Generally these values are expressed via the following parameters: - /// - escapeFrom: the scope in which an expression is being evaluated. Usually the current local - /// scope - /// - escapeTo: the scope to which the values are being escaped to. - /// - private const uint CallingMethodScope = 0; - private const uint ReturnOnlyScope = 1; - private const uint CurrentMethodScope = 2; } #nullable disable @@ -1084,19 +1061,19 @@ private bool CheckLocalValueKind(SyntaxNode node, BoundLocal local, BindValueKin internal partial class RefSafetyAnalysis { - private bool CheckLocalRefEscape(SyntaxNode node, BoundLocal local, uint escapeTo, bool checkingReceiver, BindingDiagnosticBag diagnostics) + private bool CheckLocalRefEscape(SyntaxNode node, BoundLocal local, SafeContext escapeTo, bool checkingReceiver, BindingDiagnosticBag diagnostics) { LocalSymbol localSymbol = local.LocalSymbol; // if local symbol can escape to the same or wider/shallower scope then escapeTo // then it is all ok, otherwise it is an error. - if (GetLocalScopes(localSymbol).RefEscapeScope <= escapeTo) + if (GetLocalScopes(localSymbol).RefEscapeScope.IsConvertibleTo(escapeTo)) { return true; } var inUnsafeRegion = _inUnsafeRegion; - if (escapeTo is CallingMethodScope or ReturnOnlyScope) + if (escapeTo.IsReturnable) { if (localSymbol.RefKind == RefKind.None) { @@ -1241,46 +1218,46 @@ static void reportReadOnlyParameterError(ParameterSymbol parameterSymbol, Syntax internal partial class RefSafetyAnalysis { - private static EscapeLevel? EscapeLevelFromScope(uint scope) => scope switch + private static EscapeLevel? EscapeLevelFromScope(SafeContext lifetime) => lifetime switch { - ReturnOnlyScope => EscapeLevel.ReturnOnly, - CallingMethodScope => EscapeLevel.CallingMethod, + { IsReturnOnly: true } => EscapeLevel.ReturnOnly, + { IsCallingMethod: true } => EscapeLevel.CallingMethod, _ => null, }; - private static uint GetParameterValEscape(ParameterSymbol parameter) + private static SafeContext GetParameterValEscape(ParameterSymbol parameter) { return parameter switch { - { EffectiveScope: ScopedKind.ScopedValue } => CurrentMethodScope, - { RefKind: RefKind.Out, UseUpdatedEscapeRules: true } => ReturnOnlyScope, - _ => CallingMethodScope + { EffectiveScope: ScopedKind.ScopedValue } => SafeContext.CurrentMethod, + { RefKind: RefKind.Out, UseUpdatedEscapeRules: true } => SafeContext.ReturnOnly, + _ => SafeContext.CallingMethod }; } private static EscapeLevel? GetParameterValEscapeLevel(ParameterSymbol parameter) => EscapeLevelFromScope(GetParameterValEscape(parameter)); - private static uint GetParameterRefEscape(ParameterSymbol parameter) + private static SafeContext GetParameterRefEscape(ParameterSymbol parameter) { return parameter switch { - { RefKind: RefKind.None } => CurrentMethodScope, - { EffectiveScope: ScopedKind.ScopedRef } => CurrentMethodScope, - { HasUnscopedRefAttribute: true, RefKind: RefKind.Out } => ReturnOnlyScope, - { HasUnscopedRefAttribute: true, IsThis: false } => CallingMethodScope, - _ => ReturnOnlyScope + { RefKind: RefKind.None } => SafeContext.CurrentMethod, + { EffectiveScope: ScopedKind.ScopedRef } => SafeContext.CurrentMethod, + { HasUnscopedRefAttribute: true, RefKind: RefKind.Out } => SafeContext.ReturnOnly, + { HasUnscopedRefAttribute: true, IsThis: false } => SafeContext.CallingMethod, + _ => SafeContext.ReturnOnly }; } private static EscapeLevel? GetParameterRefEscapeLevel(ParameterSymbol parameter) => EscapeLevelFromScope(GetParameterRefEscape(parameter)); - private bool CheckParameterValEscape(SyntaxNode node, ParameterSymbol parameter, uint escapeTo, BindingDiagnosticBag diagnostics) + private bool CheckParameterValEscape(SyntaxNode node, ParameterSymbol parameter, SafeContext escapeTo, BindingDiagnosticBag diagnostics) { if (_useUpdatedEscapeRules) { - if (GetParameterValEscape(parameter) > escapeTo) + if (!GetParameterValEscape(parameter).IsConvertibleTo(escapeTo)) { Error(diagnostics, _inUnsafeRegion ? ErrorCode.WRN_EscapeVariable : ErrorCode.ERR_EscapeVariable, node, parameter); return _inUnsafeRegion; @@ -1294,13 +1271,13 @@ private bool CheckParameterValEscape(SyntaxNode node, ParameterSymbol parameter, } } - private bool CheckParameterRefEscape(SyntaxNode node, BoundExpression parameter, ParameterSymbol parameterSymbol, uint escapeTo, bool checkingReceiver, BindingDiagnosticBag diagnostics) + private bool CheckParameterRefEscape(SyntaxNode node, BoundExpression parameter, ParameterSymbol parameterSymbol, SafeContext escapeTo, bool checkingReceiver, BindingDiagnosticBag diagnostics) { var refSafeToEscape = GetParameterRefEscape(parameterSymbol); - if (refSafeToEscape > escapeTo) + if (!refSafeToEscape.IsConvertibleTo(escapeTo)) { var isRefScoped = parameterSymbol.EffectiveScope == ScopedKind.ScopedRef; - Debug.Assert(parameterSymbol.RefKind == RefKind.None || isRefScoped || refSafeToEscape == ReturnOnlyScope); + Debug.Assert(parameterSymbol.RefKind == RefKind.None || isRefScoped || refSafeToEscape.IsReturnOnly); var inUnsafeRegion = _inUnsafeRegion; if (parameter is BoundThisReference) @@ -1314,14 +1291,14 @@ private bool CheckParameterRefEscape(SyntaxNode node, BoundExpression parameter, { (checkingReceiver: true, isRefScoped: true, inUnsafeRegion: false, _) => (ErrorCode.ERR_RefReturnScopedParameter2, parameter.Syntax), (checkingReceiver: true, isRefScoped: true, inUnsafeRegion: true, _) => (ErrorCode.WRN_RefReturnScopedParameter2, parameter.Syntax), - (checkingReceiver: true, isRefScoped: false, inUnsafeRegion: false, ReturnOnlyScope) => (ErrorCode.ERR_RefReturnOnlyParameter2, parameter.Syntax), - (checkingReceiver: true, isRefScoped: false, inUnsafeRegion: true, ReturnOnlyScope) => (ErrorCode.WRN_RefReturnOnlyParameter2, parameter.Syntax), + (checkingReceiver: true, isRefScoped: false, inUnsafeRegion: false, { IsReturnOnly: true }) => (ErrorCode.ERR_RefReturnOnlyParameter2, parameter.Syntax), + (checkingReceiver: true, isRefScoped: false, inUnsafeRegion: true, { IsReturnOnly: true }) => (ErrorCode.WRN_RefReturnOnlyParameter2, parameter.Syntax), (checkingReceiver: true, isRefScoped: false, inUnsafeRegion: false, _) => (ErrorCode.ERR_RefReturnParameter2, parameter.Syntax), (checkingReceiver: true, isRefScoped: false, inUnsafeRegion: true, _) => (ErrorCode.WRN_RefReturnParameter2, parameter.Syntax), (checkingReceiver: false, isRefScoped: true, inUnsafeRegion: false, _) => (ErrorCode.ERR_RefReturnScopedParameter, node), (checkingReceiver: false, isRefScoped: true, inUnsafeRegion: true, _) => (ErrorCode.WRN_RefReturnScopedParameter, node), - (checkingReceiver: false, isRefScoped: false, inUnsafeRegion: false, ReturnOnlyScope) => (ErrorCode.ERR_RefReturnOnlyParameter, node), - (checkingReceiver: false, isRefScoped: false, inUnsafeRegion: true, ReturnOnlyScope) => (ErrorCode.WRN_RefReturnOnlyParameter, node), + (checkingReceiver: false, isRefScoped: false, inUnsafeRegion: false, { IsReturnOnly: true }) => (ErrorCode.ERR_RefReturnOnlyParameter, node), + (checkingReceiver: false, isRefScoped: false, inUnsafeRegion: true, { IsReturnOnly: true }) => (ErrorCode.WRN_RefReturnOnlyParameter, node), (checkingReceiver: false, isRefScoped: false, inUnsafeRegion: false, _) => (ErrorCode.ERR_RefReturnParameter, node), (checkingReceiver: false, isRefScoped: false, inUnsafeRegion: true, _) => (ErrorCode.WRN_RefReturnParameter, node) }; @@ -1492,14 +1469,14 @@ private bool CheckSimpleAssignmentValueKind(SyntaxNode node, BoundAssignmentOper internal partial class RefSafetyAnalysis { - private uint GetFieldRefEscape(BoundFieldAccess fieldAccess, uint scopeOfTheContainingExpression) + private SafeContext GetFieldRefEscape(BoundFieldAccess fieldAccess, SafeContext scopeOfTheContainingExpression) { var fieldSymbol = fieldAccess.FieldSymbol; // fields that are static or belong to reference types can ref escape anywhere if (fieldSymbol.IsStatic || fieldSymbol.ContainingType.IsReferenceType) { - return CallingMethodScope; + return SafeContext.CallingMethod; } if (_useUpdatedEscapeRules) @@ -1515,7 +1492,7 @@ private uint GetFieldRefEscape(BoundFieldAccess fieldAccess, uint scopeOfTheCont return GetRefEscape(fieldAccess.ReceiverOpt, scopeOfTheContainingExpression); } - private bool CheckFieldRefEscape(SyntaxNode node, BoundFieldAccess fieldAccess, uint escapeFrom, uint escapeTo, BindingDiagnosticBag diagnostics) + private bool CheckFieldRefEscape(SyntaxNode node, BoundFieldAccess fieldAccess, SafeContext escapeFrom, SafeContext escapeTo, BindingDiagnosticBag diagnostics) { var fieldSymbol = fieldAccess.FieldSymbol; // fields that are static or belong to reference types can ref escape anywhere @@ -1539,7 +1516,7 @@ private bool CheckFieldRefEscape(SyntaxNode node, BoundFieldAccess fieldAccess, return CheckRefEscape(node, fieldAccess.ReceiverOpt, escapeFrom, escapeTo, checkingReceiver: true, diagnostics: diagnostics); } - private bool CheckFieldLikeEventRefEscape(SyntaxNode node, BoundEventAccess eventAccess, uint escapeFrom, uint escapeTo, BindingDiagnosticBag diagnostics) + private bool CheckFieldLikeEventRefEscape(SyntaxNode node, BoundEventAccess eventAccess, SafeContext escapeFrom, SafeContext escapeTo, BindingDiagnosticBag diagnostics) { var eventSymbol = eventAccess.EventSymbol; @@ -1937,9 +1914,9 @@ private bool IsBadBaseAccess(SyntaxNode node, BoundExpression receiverOpt, Symbo internal partial class RefSafetyAnalysis { - internal uint GetInterpolatedStringHandlerConversionEscapeScope( + internal SafeContext GetInterpolatedStringHandlerConversionEscapeScope( BoundExpression expression, - uint scopeOfTheContainingExpression) + SafeContext scopeOfTheContainingExpression) { var data = expression.GetInterpolatedStringHandlerData(); #if DEBUG @@ -1949,7 +1926,7 @@ internal uint GetInterpolatedStringHandlerConversionEscapeScope( var previousVisited = _visited; _visited = null; #endif - uint escapeScope = GetValEscape(data.Construction, scopeOfTheContainingExpression); + SafeContext escapeScope = GetValEscape(data.Construction, scopeOfTheContainingExpression); #if DEBUG _visited = previousVisited; #endif @@ -1959,8 +1936,8 @@ internal uint GetInterpolatedStringHandlerConversionEscapeScope( foreach (var argument in arguments) { - uint argEscape = GetValEscape(argument, scopeOfTheContainingExpression); - escapeScope = Math.Max(escapeScope, argEscape); + SafeContext argEscape = GetValEscape(argument, scopeOfTheContainingExpression); + escapeScope = escapeScope.Intersect(argEscape); } arguments.Free(); @@ -1977,7 +1954,7 @@ internal uint GetInterpolatedStringHandlerConversionEscapeScope( /// NOTE: we need scopeOfTheContainingExpression as some expressions such as optional in parameters or ref dynamic behave as /// local variables declared at the scope of the invocation. /// - private uint GetInvocationEscapeScope( + private SafeContext GetInvocationEscapeScope( in MethodInfo methodInfo, BoundExpression? receiver, ThreeState receiverIsSubjectToCloning, @@ -1985,7 +1962,7 @@ private uint GetInvocationEscapeScope( ImmutableArray argsOpt, ImmutableArray argRefKindsOpt, ImmutableArray argsToParamsOpt, - uint scopeOfTheContainingExpression, + SafeContext scopeOfTheContainingExpression, bool isRefEscape ) { @@ -2010,7 +1987,7 @@ bool isRefEscape //• the safe-to-escape of all argument expressions(including the receiver) // - uint escapeScope = CallingMethodScope; + SafeContext escapeScope = SafeContext.CallingMethod; var escapeValues = ArrayBuilder.GetInstance(); GetEscapeValuesForOldRules( in methodInfo, @@ -2037,15 +2014,15 @@ bool isRefEscape // // val escape scope is the narrowest of // - val escape of all byval arguments (refs cannot be wrapped into values, so their ref escape is irrelevant, only use val escapes) - uint argumentEscape = (isRefEscape, argumentIsRefEscape) switch + SafeContext argumentEscape = (isRefEscape, argumentIsRefEscape) switch { (true, true) => GetRefEscape(argument, scopeOfTheContainingExpression), (false, false) => GetValEscape(argument, scopeOfTheContainingExpression), _ => escapeScope }; - escapeScope = Math.Max(escapeScope, argumentEscape); - if (escapeScope >= scopeOfTheContainingExpression) + escapeScope = escapeScope.Intersect(argumentEscape); + if (scopeOfTheContainingExpression.IsConvertibleTo(escapeScope)) { // can't get any worse return escapeScope; @@ -2060,13 +2037,13 @@ bool isRefEscape // check receiver if ref-like if (methodInfo.Method?.RequiresInstanceReceiver == true && receiver?.Type?.IsRefLikeOrAllowsRefLikeType() == true) { - escapeScope = Math.Max(escapeScope, GetValEscape(receiver, scopeOfTheContainingExpression)); + escapeScope = escapeScope.Intersect(GetValEscape(receiver, scopeOfTheContainingExpression)); } return escapeScope; } - private uint GetInvocationEscapeWithUpdatedRules( + private SafeContext GetInvocationEscapeWithUpdatedRules( in MethodInfo methodInfo, BoundExpression? receiver, ThreeState receiverIsSubjectToCloning, @@ -2074,11 +2051,11 @@ private uint GetInvocationEscapeWithUpdatedRules( ImmutableArray argsOpt, ImmutableArray argRefKindsOpt, ImmutableArray argsToParamsOpt, - uint scopeOfTheContainingExpression, + SafeContext scopeOfTheContainingExpression, bool isRefEscape) { //by default it is safe to escape - uint escapeScope = CallingMethodScope; + SafeContext escapeScope = SafeContext.CallingMethod; var argsAndParamsAll = ArrayBuilder.GetInstance(); GetFilteredInvocationArgumentsForEscapeWithUpdatedRules( @@ -2105,12 +2082,12 @@ private uint GetInvocationEscapeWithUpdatedRules( (param is { RefKind: not RefKind.None, Type: { } type } && type.IsRefLikeOrAllowsRefLikeType())) && isArgumentRefEscape == isRefEscape)) { - uint argEscape = isArgumentRefEscape ? + SafeContext argEscape = isArgumentRefEscape ? GetRefEscape(argument, scopeOfTheContainingExpression) : GetValEscape(argument, scopeOfTheContainingExpression); - escapeScope = Math.Max(escapeScope, argEscape); - if (escapeScope >= scopeOfTheContainingExpression) + escapeScope = escapeScope.Intersect(argEscape); + if (scopeOfTheContainingExpression.IsConvertibleTo(escapeScope)) { // can't get any worse break; @@ -2140,8 +2117,8 @@ private bool CheckInvocationEscape( ImmutableArray argRefKindsOpt, ImmutableArray argsToParamsOpt, bool checkingReceiver, - uint escapeFrom, - uint escapeTo, + SafeContext escapeFrom, + SafeContext escapeTo, BindingDiagnosticBag diagnostics, bool isRefEscape ) @@ -2233,8 +2210,8 @@ private bool CheckInvocationEscapeWithUpdatedRules( ImmutableArray argRefKindsOpt, ImmutableArray argsToParamsOpt, bool checkingReceiver, - uint escapeFrom, - uint escapeTo, + SafeContext escapeFrom, + SafeContext escapeTo, BindingDiagnosticBag diagnostics, bool isRefEscape) { @@ -2768,7 +2745,7 @@ private bool ShouldInferDeclarationExpressionValEscape(BoundExpression argument, _ => null }; if (symbol is SourceLocalSymbol local && - GetLocalScopes(local).ValEscapeScope == CallingMethodScope) + GetLocalScopes(local).ValEscapeScope.IsCallingMethod) { localSymbol = local; return true; @@ -2797,7 +2774,7 @@ private bool CheckInvocationArgMixing( ImmutableArray argsOpt, ImmutableArray argRefKindsOpt, ImmutableArray argsToParamsOpt, - uint scopeOfTheContainingExpression, + SafeContext scopeOfTheContainingExpression, BindingDiagnosticBag diagnostics) { if (methodInfo.UseUpdatedEscapeRules) @@ -2818,7 +2795,7 @@ private bool CheckInvocationArgMixing( } // widest possible escape via writeable ref-like receiver or ref/out argument. - uint escapeTo = scopeOfTheContainingExpression; + SafeContext escapeTo = scopeOfTheContainingExpression; // collect all writeable ref-like arguments, including receiver var escapeArguments = ArrayBuilder.GetInstance(); @@ -2850,7 +2827,7 @@ private bool CheckInvocationArgMixing( && !argument.IsDiscardExpression() && argument.Type?.IsRefLikeOrAllowsRefLikeType() == true) { - escapeTo = Math.Min(escapeTo, GetValEscape(argument, scopeOfTheContainingExpression)); + escapeTo = escapeTo.Union(GetValEscape(argument, scopeOfTheContainingExpression)); } } @@ -2858,12 +2835,12 @@ private bool CheckInvocationArgMixing( // track the widest scope that arguments could safely escape to. // use this scope as the inferred STE of declaration expressions. - var inferredDestinationValEscape = CallingMethodScope; + var inferredDestinationValEscape = SafeContext.CallingMethod; foreach (var (parameter, argument, _) in escapeArguments) { // in the old rules, we assume that refs cannot escape into ref struct variables. // e.g. in `dest = M(ref arg)`, we assume `ref arg` will not escape into `dest`, but `arg` might. - inferredDestinationValEscape = Math.Max(inferredDestinationValEscape, GetValEscape(argument, scopeOfTheContainingExpression)); + inferredDestinationValEscape = inferredDestinationValEscape.Intersect(GetValEscape(argument, scopeOfTheContainingExpression)); if (!hasMixingError && !CheckValEscape(argument.Syntax, argument, scopeOfTheContainingExpression, escapeTo, false, diagnostics)) { string parameterName = GetInvocationParameterName(parameter); @@ -2897,7 +2874,7 @@ private bool CheckInvocationArgMixingWithUpdatedRules( ImmutableArray argsOpt, ImmutableArray argRefKindsOpt, ImmutableArray argsToParamsOpt, - uint scopeOfTheContainingExpression, + SafeContext scopeOfTheContainingExpression, BindingDiagnosticBag diagnostics) { var mixableArguments = ArrayBuilder.GetInstance(); @@ -2920,11 +2897,6 @@ private bool CheckInvocationArgMixingWithUpdatedRules( var toArgEscape = GetValEscape(mixableArg.Argument, scopeOfTheContainingExpression); foreach (var (fromParameter, fromArg, escapeKind, isRefEscape) in escapeValues) { - if (mixableArg.Parameter is not null && object.ReferenceEquals(mixableArg.Parameter, fromParameter)) - { - continue; - } - // This checks to see if the EscapeValue could ever be assigned to this argument based // on comparing the EscapeLevel of both. If this could never be assigned due to // this then we don't need to consider it for MAMM analysis. @@ -2961,10 +2933,10 @@ void inferDeclarationExpressionValEscape() { // find the widest scope that arguments could safely escape to. // use this scope as the inferred STE of declaration expressions. - var inferredDestinationValEscape = CallingMethodScope; + var inferredDestinationValEscape = SafeContext.CallingMethod; foreach (var (_, fromArg, _, isRefEscape) in escapeValues) { - inferredDestinationValEscape = Math.Max(inferredDestinationValEscape, isRefEscape + inferredDestinationValEscape = inferredDestinationValEscape.Intersect(isRefEscape ? GetRefEscape(fromArg, scopeOfTheContainingExpression) : GetValEscape(fromArg, scopeOfTheContainingExpression)); } @@ -3235,9 +3207,9 @@ private static ErrorCode GetStandardLvalueError(BindValueKind kind) internal partial class RefSafetyAnalysis { - private static ErrorCode GetStandardRValueRefEscapeError(uint escapeTo) + private static ErrorCode GetStandardRValueRefEscapeError(SafeContext escapeTo) { - if (escapeTo is CallingMethodScope or ReturnOnlyScope) + if (escapeTo.IsReturnable) { return ErrorCode.ERR_RefReturnLvalueExpected; } @@ -3319,7 +3291,7 @@ internal partial class RefSafetyAnalysis /// /// Checks whether given expression can escape from the current scope to the . /// - internal void ValidateEscape(BoundExpression expr, uint escapeTo, bool isByRef, BindingDiagnosticBag diagnostics) + internal void ValidateEscape(BoundExpression expr, SafeContext escapeTo, bool isByRef, BindingDiagnosticBag diagnostics) { // The result of escape analysis is affected by the expression's type. // We can't do escape analysis on expressions which lack a type, such as 'target typed new()', until they are converted. @@ -3342,7 +3314,7 @@ internal void ValidateEscape(BoundExpression expr, uint escapeTo, bool isByRef, /// There are few cases where RValues are permitted to be passed by reference which implies that a temporary local proxy is passed instead. /// We reflect such behavior by constraining the escape value to the narrowest scope possible. /// - internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpression) + internal SafeContext GetRefEscape(BoundExpression expr, SafeContext scopeOfTheContainingExpression) { #if DEBUG AssertVisited(expr); @@ -3351,13 +3323,13 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres // cannot infer anything from errors if (expr.HasAnyErrors) { - return CallingMethodScope; + return SafeContext.CallingMethod; } // cannot infer anything from Void (broken code) if (expr.Type?.GetSpecialTypeSafe() == SpecialType.System_Void) { - return CallingMethodScope; + return SafeContext.CallingMethod; } // constants/literals cannot ref-escape current scope @@ -3374,13 +3346,13 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres case BoundKind.PointerIndirectionOperator: case BoundKind.PointerElementAccess: // array elements and pointer dereferencing are readwrite variables - return CallingMethodScope; + return SafeContext.CallingMethod; case BoundKind.RefValueOperator: // The undocumented __refvalue(tr, T) expression results in an lvalue of type T. // for compat reasons it is not ref-returnable (since TypedReference is not val-returnable) // it can, however, ref-escape to any other level (since TypedReference can val-escape to any other level) - return CurrentMethodScope; + return SafeContext.CurrentMethod; case BoundKind.DiscardExpression: // same as write-only byval local @@ -3414,8 +3386,8 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres if (conditional.IsRef) { // ref conditional defers to its operands - return Math.Max(GetRefEscape(conditional.Consequence, scopeOfTheContainingExpression), - GetRefEscape(conditional.Alternative, scopeOfTheContainingExpression)); + return GetRefEscape(conditional.Consequence, scopeOfTheContainingExpression) + .Intersect(GetRefEscape(conditional.Alternative, scopeOfTheContainingExpression)); } // otherwise it is an RValue @@ -3437,7 +3409,7 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres // field-like events that are static or belong to reference types can ref escape anywhere if (eventSymbol.IsStatic || eventSymbol.ContainingType.IsReferenceType) { - return CallingMethodScope; + return SafeContext.CallingMethod; } // for other events defer to the receiver. @@ -3527,7 +3499,7 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres case BoundArrayAccess: // array elements are readwrite variables - return CallingMethodScope; + return SafeContext.CallingMethod; case BoundCall call: var methodSymbol = call.Method; @@ -3650,7 +3622,7 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres /// The result indicates whether the escape is possible. /// Additionally, the method emits diagnostics (possibly more than one, recursively) that would help identify the cause for the failure. /// - internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeFrom, uint escapeTo, bool checkingReceiver, BindingDiagnosticBag diagnostics) + internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, SafeContext escapeFrom, SafeContext escapeTo, bool checkingReceiver, BindingDiagnosticBag diagnostics) { #if DEBUG AssertVisited(expr); @@ -3658,7 +3630,7 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF Debug.Assert(!checkingReceiver || expr.Type.IsValueType || expr.Type.IsTypeParameter()); - if (escapeTo >= escapeFrom) + if (escapeFrom.IsConvertibleTo(escapeTo)) { // escaping to same or narrower scope is ok. return true; @@ -3694,7 +3666,7 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF case BoundKind.RefValueOperator: // The undocumented __refvalue(tr, T) expression results in an lvalue of type T. // for compat reasons it is not ref-returnable (since TypedReference is not val-returnable) - if (escapeTo is CallingMethodScope or ReturnOnlyScope) + if (escapeTo.IsReturnable) { break; } @@ -3723,7 +3695,7 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF case BoundKind.CapturedReceiverPlaceholder: // Equivalent to a non-ref local with the underlying receiver as an initializer provided at declaration - if (((BoundCapturedReceiverPlaceholder)expr).LocalScopeDepth <= escapeTo) + if (((BoundCapturedReceiverPlaceholder)expr).LocalScopeDepth.IsConvertibleTo(escapeTo)) { return true; } @@ -4019,12 +3991,12 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF return false; } - internal uint GetBroadestValEscape(BoundTupleExpression expr, uint scopeOfTheContainingExpression) + internal SafeContext GetBroadestValEscape(BoundTupleExpression expr, SafeContext scopeOfTheContainingExpression) { - uint broadest = scopeOfTheContainingExpression; + SafeContext broadest = scopeOfTheContainingExpression; foreach (var element in expr.Arguments) { - uint valEscape; + SafeContext valEscape; if (element is BoundTupleExpression te) { valEscape = GetBroadestValEscape(te, scopeOfTheContainingExpression); @@ -4034,7 +4006,7 @@ internal uint GetBroadestValEscape(BoundTupleExpression expr, uint scopeOfTheCon valEscape = GetValEscape(element, scopeOfTheContainingExpression); } - broadest = Math.Min(broadest, valEscape); + broadest = broadest.Union(valEscape); } return broadest; @@ -4045,7 +4017,7 @@ internal uint GetBroadestValEscape(BoundTupleExpression expr, uint scopeOfTheCon /// /// NOTE: unless the type of expression is ref-like, the result is Binder.ExternalScope since ordinary values can always be returned from methods. /// - internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpression) + internal SafeContext GetValEscape(BoundExpression expr, SafeContext scopeOfTheContainingExpression) { #if DEBUG AssertVisited(expr); @@ -4054,19 +4026,19 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres // cannot infer anything from errors if (expr.HasAnyErrors) { - return CallingMethodScope; + return SafeContext.CallingMethod; } // constants/literals cannot refer to local state if (expr.ConstantValueOpt != null) { - return CallingMethodScope; + return SafeContext.CallingMethod; } // to have local-referring values an expression must have a ref-like type if (expr.Type?.IsRefLikeOrAllowsRefLikeType() != true) { - return CallingMethodScope; + return SafeContext.CallingMethod; } // cover case that can refer to local state @@ -4081,7 +4053,7 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres case BoundKind.DefaultExpression: case BoundKind.Utf8String: // always returnable - return CallingMethodScope; + return SafeContext.CallingMethod; case BoundKind.Parameter: return GetParameterValEscape(((BoundParameter)expr).ParameterSymbol); @@ -4089,7 +4061,7 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres case BoundKind.FromEndIndexExpression: // We are going to call a constructor that takes an integer and a bool. Cannot leak any references through them. // always returnable - return CallingMethodScope; + return SafeContext.CallingMethod; case BoundKind.TupleLiteral: case BoundKind.ConvertedTupleLiteral: @@ -4101,10 +4073,10 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres // for compat reasons // NB: it also means can`t assign stackalloc spans to a __refvalue // we are ok with that. - return CallingMethodScope; + return SafeContext.CallingMethod; case BoundKind.DiscardExpression: - return CallingMethodScope; + return SafeContext.CallingMethod; case BoundKind.DeconstructValuePlaceholder: case BoundKind.InterpolatedStringArgumentPlaceholder: @@ -4121,7 +4093,7 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres case BoundKind.StackAllocArrayCreation: case BoundKind.ConvertedStackAllocExpression: - return CurrentMethodScope; + return SafeContext.CurrentMethod; case BoundKind.ConditionalOperator: var conditional = (BoundConditionalOperator)expr; @@ -4136,14 +4108,13 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres } // val conditional gets narrowest of its operands - return Math.Max(consEscape, - GetValEscape(conditional.Alternative, scopeOfTheContainingExpression)); + return consEscape.Intersect(GetValEscape(conditional.Alternative, scopeOfTheContainingExpression)); case BoundKind.NullCoalescingOperator: var coalescingOp = (BoundNullCoalescingOperator)expr; - return Math.Max(GetValEscape(coalescingOp.LeftOperand, scopeOfTheContainingExpression), - GetValEscape(coalescingOp.RightOperand, scopeOfTheContainingExpression)); + return GetValEscape(coalescingOp.LeftOperand, scopeOfTheContainingExpression) + .Intersect(GetValEscape(coalescingOp.RightOperand, scopeOfTheContainingExpression)); case BoundKind.FieldAccess: var fieldAccess = (BoundFieldAccess)expr; @@ -4152,7 +4123,7 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres if (fieldSymbol.IsStatic || !fieldSymbol.ContainingType.IsRefLikeType) { // Already an error state. - return CallingMethodScope; + return SafeContext.CallingMethod; } // for ref-like fields defer to the receiver. @@ -4301,7 +4272,7 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres var initializerOpt = objectCreation.InitializerExpressionOpt; if (initializerOpt != null) { - escape = Math.Max(escape, GetValEscape(initializerOpt, scopeOfTheContainingExpression)); + escape = escape.Intersect(GetValEscape(initializerOpt, scopeOfTheContainingExpression)); } return escape; @@ -4311,12 +4282,12 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres { var newT = (BoundNewT)expr; // By default it is safe to escape - var escape = CallingMethodScope; + var escape = SafeContext.CallingMethod; var initializerOpt = newT.InitializerExpressionOpt; if (initializerOpt != null) { - escape = Math.Max(escape, GetValEscape(initializerOpt, scopeOfTheContainingExpression)); + escape = escape.Intersect(GetValEscape(initializerOpt, scopeOfTheContainingExpression)); } return escape; @@ -4325,8 +4296,8 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres case BoundKind.WithExpression: var withExpression = (BoundWithExpression)expr; - return Math.Max(GetValEscape(withExpression.Receiver, scopeOfTheContainingExpression), - GetValEscape(withExpression.InitializerExpression, scopeOfTheContainingExpression)); + return GetValEscape(withExpression.Receiver, scopeOfTheContainingExpression) + .Intersect(GetValEscape(withExpression.InitializerExpression, scopeOfTheContainingExpression)); case BoundKind.UnaryOperator: var unaryOperator = (BoundUnaryOperator)expr; @@ -4358,8 +4329,8 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres if (conversion.ConversionKind == ConversionKind.CollectionExpression) { return HasLocalScope((BoundCollectionExpression)conversion.Operand) ? - CurrentMethodScope : - CallingMethodScope; + SafeContext.CurrentMethod : + SafeContext.CallingMethod; } if (conversion.Conversion.IsInlineArray) @@ -4429,8 +4400,8 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres isRefEscape: false); } - return Math.Max(GetValEscape(compound.Left, scopeOfTheContainingExpression), - GetValEscape(compound.Right, scopeOfTheContainingExpression)); + return GetValEscape(compound.Left, scopeOfTheContainingExpression) + .Intersect(GetValEscape(compound.Right, scopeOfTheContainingExpression)); case BoundKind.BinaryOperator: var binary = (BoundBinaryOperator)expr; @@ -4449,14 +4420,14 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres isRefEscape: false); } - return Math.Max(GetValEscape(binary.Left, scopeOfTheContainingExpression), - GetValEscape(binary.Right, scopeOfTheContainingExpression)); + return GetValEscape(binary.Left, scopeOfTheContainingExpression) + .Intersect(GetValEscape(binary.Right, scopeOfTheContainingExpression)); case BoundKind.RangeExpression: var range = (BoundRangeExpression)expr; - return Math.Max((range.LeftOperandOpt is { } left ? GetValEscape(left, scopeOfTheContainingExpression) : CallingMethodScope), - (range.RightOperandOpt is { } right ? GetValEscape(right, scopeOfTheContainingExpression) : CallingMethodScope)); + return (range.LeftOperandOpt is { } left ? GetValEscape(left, scopeOfTheContainingExpression) : SafeContext.CallingMethod) + .Intersect(range.RightOperandOpt is { } right ? GetValEscape(right, scopeOfTheContainingExpression) : SafeContext.CallingMethod); case BoundKind.UserDefinedConditionalLogicalOperator: var uo = (BoundUserDefinedConditionalLogicalOperator)expr; @@ -4515,7 +4486,7 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres case BoundKind.PointerElementAccess: case BoundKind.PointerIndirectionOperator: // Unsafe code will always be allowed to escape. - return CallingMethodScope; + return SafeContext.CallingMethod; case BoundKind.AsOperator: case BoundKind.AwaitExpression: @@ -4589,12 +4560,12 @@ private bool HasLocalScope(BoundCollectionExpression expr) } } - private uint GetTupleValEscape(ImmutableArray elements, uint scopeOfTheContainingExpression) + private SafeContext GetTupleValEscape(ImmutableArray elements, SafeContext scopeOfTheContainingExpression) { - uint narrowestScope = scopeOfTheContainingExpression; + SafeContext narrowestScope = scopeOfTheContainingExpression; foreach (var element in elements) { - narrowestScope = Math.Max(narrowestScope, GetValEscape(element, scopeOfTheContainingExpression)); + narrowestScope = narrowestScope.Intersect(GetValEscape(element, scopeOfTheContainingExpression)); } return narrowestScope; @@ -4606,21 +4577,21 @@ private uint GetTupleValEscape(ImmutableArray elements, uint sc /// passed to an indexer for example only matter if they can escape into the receiver /// as a stored field. /// - private uint GetValEscapeOfObjectInitializer(BoundObjectInitializerExpression initExpr, uint scopeOfTheContainingExpression) + private SafeContext GetValEscapeOfObjectInitializer(BoundObjectInitializerExpression initExpr, SafeContext scopeOfTheContainingExpression) { - var result = CallingMethodScope; + var result = SafeContext.CallingMethod; foreach (var expr in initExpr.Initializers) { var exprResult = GetValEscapeOfObjectMemberInitializer(expr, scopeOfTheContainingExpression); - result = Math.Max(result, exprResult); + result = result.Intersect(exprResult); } return result; } - private uint GetValEscapeOfObjectMemberInitializer(BoundExpression expr, uint scopeOfTheContainingExpression) + private SafeContext GetValEscapeOfObjectMemberInitializer(BoundExpression expr, SafeContext scopeOfTheContainingExpression) { - uint result; + SafeContext result; if (expr.Kind == BoundKind.AssignmentOperator) { var assignment = (BoundAssignmentOperator)expr; @@ -4643,23 +4614,23 @@ private uint GetValEscapeOfObjectMemberInitializer(BoundExpression expr, uint sc return result; - uint getIndexerEscape( + SafeContext getIndexerEscape( PropertySymbol indexer, BoundObjectInitializerMember expr, - uint rightEscapeScope) + SafeContext rightEscapeScope) { Debug.Assert(expr.AccessorKind != AccessorKind.Unknown); var methodInfo = MethodInfo.Create(indexer, expr.AccessorKind); if (methodInfo.Method is null) { - return CallingMethodScope; + return SafeContext.CallingMethod; } // If the indexer is readonly then none of the arguments can contribute to // the receiver escape if (methodInfo.Method.IsEffectivelyReadOnly) { - return CallingMethodScope; + return SafeContext.CallingMethod; } var escapeValues = ArrayBuilder.GetInstance(); @@ -4676,7 +4647,7 @@ uint getIndexerEscape( mixableArguments: null, escapeValues); - uint receiverEscapeScope = CallingMethodScope; + SafeContext receiverEscapeScope = SafeContext.CallingMethod; foreach (var escapeValue in escapeValues) { // This is a call to an indexer so the ref escape scope can only impact the escape value if it @@ -4686,25 +4657,25 @@ uint getIndexerEscape( continue; } - uint escapeScope = escapeValue.IsRefEscape + SafeContext escapeScope = escapeValue.IsRefEscape ? GetRefEscape(escapeValue.Argument, scopeOfTheContainingExpression) : GetValEscape(escapeValue.Argument, scopeOfTheContainingExpression); - receiverEscapeScope = Math.Max(escapeScope, receiverEscapeScope); + receiverEscapeScope = escapeScope.Intersect(receiverEscapeScope); } escapeValues.Free(); - return Math.Max(receiverEscapeScope, rightEscapeScope); + return receiverEscapeScope.Intersect(rightEscapeScope); } - uint getPropertyEscape( + SafeContext getPropertyEscape( PropertySymbol property, - uint rightEscapeScope) + SafeContext rightEscapeScope) { var accessorKind = property.RefKind == RefKind.None ? AccessorKind.Set : AccessorKind.Get; var methodInfo = MethodInfo.Create(property, accessorKind); if (methodInfo.Method is null || methodInfo.Method.IsEffectivelyReadOnly) { - return CallingMethodScope; + return SafeContext.CallingMethod; } return rightEscapeScope; @@ -4713,12 +4684,12 @@ uint getPropertyEscape( #nullable disable - private uint GetValEscape(ImmutableArray expressions, uint scopeOfTheContainingExpression) + private SafeContext GetValEscape(ImmutableArray expressions, SafeContext scopeOfTheContainingExpression) { - var result = CallingMethodScope; + var result = SafeContext.CallingMethod; foreach (var expression in expressions) { - result = Math.Max(result, GetValEscape(expression, scopeOfTheContainingExpression)); + result = result.Intersect(GetValEscape(expression, scopeOfTheContainingExpression)); } return result; @@ -4729,7 +4700,7 @@ private uint GetValEscape(ImmutableArray expressions, uint scop /// The result indicates whether the escape is possible. /// Additionally, the method emits diagnostics (possibly more than one, recursively) that would help identify the cause for the failure. /// - internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeFrom, uint escapeTo, bool checkingReceiver, BindingDiagnosticBag diagnostics) + internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, SafeContext escapeFrom, SafeContext escapeTo, bool checkingReceiver, BindingDiagnosticBag diagnostics) { #if DEBUG AssertVisited(expr); @@ -4737,7 +4708,7 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF Debug.Assert(!checkingReceiver || expr.Type.IsValueType || expr.Type.IsTypeParameter()); - if (escapeTo >= escapeFrom) + if (escapeFrom.IsConvertibleTo(escapeTo)) { // escaping to same or narrower scope is ok. return true; @@ -4796,7 +4767,7 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF case BoundKind.DeconstructValuePlaceholder: case BoundKind.AwaitableValuePlaceholder: case BoundKind.InterpolatedStringArgumentPlaceholder: - if (GetPlaceholderScope((BoundValuePlaceholderBase)expr) > escapeTo) + if (!GetPlaceholderScope((BoundValuePlaceholderBase)expr).IsConvertibleTo(escapeTo)) { Error(diagnostics, inUnsafeRegion ? ErrorCode.WRN_EscapeVariable : ErrorCode.ERR_EscapeVariable, node, expr.Syntax); return inUnsafeRegion; @@ -4805,7 +4776,7 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF case BoundKind.Local: var localSymbol = ((BoundLocal)expr).LocalSymbol; - if (GetLocalScopes(localSymbol).ValEscapeScope > escapeTo) + if (!GetLocalScopes(localSymbol).ValEscapeScope.IsConvertibleTo(escapeTo)) { Error(diagnostics, inUnsafeRegion ? ErrorCode.WRN_EscapeVariable : ErrorCode.ERR_EscapeVariable, node, localSymbol); return inUnsafeRegion; @@ -4819,7 +4790,7 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF case BoundKind.StackAllocArrayCreation: case BoundKind.ConvertedStackAllocExpression: - if (escapeTo < CurrentMethodScope) + if (!SafeContext.CurrentMethod.IsConvertibleTo(escapeTo)) { Error(diagnostics, inUnsafeRegion ? ErrorCode.WRN_EscapeStackAlloc : ErrorCode.ERR_EscapeStackAlloc, node, expr.Type); return inUnsafeRegion; @@ -5128,7 +5099,7 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF if (conversion.ConversionKind == ConversionKind.CollectionExpression) { - if (HasLocalScope((BoundCollectionExpression)conversion.Operand) && escapeTo < CurrentMethodScope) + if (HasLocalScope((BoundCollectionExpression)conversion.Operand) && !SafeContext.CurrentMethod.IsConvertibleTo(escapeTo)) { Error(diagnostics, ErrorCode.ERR_CollectionExpressionEscape, node, expr.Type); return false; @@ -5561,7 +5532,7 @@ private SignatureOnlyMethodSymbol GetInlineArrayConversionEquivalentSignatureMet return equivalentSignatureMethod; } - private bool CheckTupleValEscape(ImmutableArray elements, uint escapeFrom, uint escapeTo, BindingDiagnosticBag diagnostics) + private bool CheckTupleValEscape(ImmutableArray elements, SafeContext escapeFrom, SafeContext escapeTo, BindingDiagnosticBag diagnostics) { foreach (var element in elements) { @@ -5576,11 +5547,11 @@ private bool CheckTupleValEscape(ImmutableArray elements, uint #nullable enable - private bool CheckValEscapeOfObjectInitializer(BoundObjectInitializerExpression initExpr, uint escapeFrom, uint escapeTo, BindingDiagnosticBag diagnostics) + private bool CheckValEscapeOfObjectInitializer(BoundObjectInitializerExpression initExpr, SafeContext escapeFrom, SafeContext escapeTo, BindingDiagnosticBag diagnostics) { foreach (var expr in initExpr.Initializers) { - if (GetValEscapeOfObjectMemberInitializer(expr, escapeFrom) > escapeTo) + if (!GetValEscapeOfObjectMemberInitializer(expr, escapeFrom).IsConvertibleTo(escapeTo)) { Error(diagnostics, _inUnsafeRegion ? ErrorCode.WRN_EscapeVariable : ErrorCode.ERR_EscapeVariable, initExpr.Syntax, expr.Syntax); return false; @@ -5592,7 +5563,7 @@ private bool CheckValEscapeOfObjectInitializer(BoundObjectInitializerExpression #nullable disable - private bool CheckValEscape(ImmutableArray expressions, uint escapeFrom, uint escapeTo, BindingDiagnosticBag diagnostics) + private bool CheckValEscape(ImmutableArray expressions, SafeContext escapeFrom, SafeContext escapeTo, BindingDiagnosticBag diagnostics) { foreach (var expression in expressions) { @@ -5605,7 +5576,7 @@ private bool CheckValEscape(ImmutableArray expressions, uint es return true; } - private bool CheckInterpolatedStringHandlerConversionEscape(BoundExpression expression, uint escapeFrom, uint escapeTo, BindingDiagnosticBag diagnostics) + private bool CheckInterpolatedStringHandlerConversionEscape(BoundExpression expression, SafeContext escapeFrom, SafeContext escapeTo, BindingDiagnosticBag diagnostics) { var data = expression.GetInterpolatedStringHandlerData(); @@ -5691,250 +5662,4 @@ void getParts(BoundInterpolatedString interpolatedString) } } } - - internal partial class Binder - { - internal enum AddressKind - { - // reference may be written to - Writeable, - - // reference itself will not be written to, but may be used for call, callvirt. - // for all purposes it is the same as Writeable, except when fetching an address of an array element - // where it results in a ".readonly" prefix to deal with array covariance. - Constrained, - - // reference itself will not be written to, nor it will be used to modify fields. - ReadOnly, - - // same as ReadOnly, but we are not supposed to get a reference to a clone - // regardless of compat settings. - ReadOnlyStrict, - } - - internal static bool IsAnyReadOnly(AddressKind addressKind) => addressKind >= AddressKind.ReadOnly; - - /// - /// Checks if expression directly or indirectly represents a value with its own home. In - /// such cases it is possible to get a reference without loading into a temporary. - /// - internal static bool HasHome( - BoundExpression expression, - AddressKind addressKind, - Symbol containingSymbol, - bool peVerifyCompatEnabled, - HashSet stackLocalsOpt) - { - Debug.Assert(containingSymbol is object); - - switch (expression.Kind) - { - case BoundKind.ArrayAccess: - if (addressKind == AddressKind.ReadOnly && !expression.Type.IsValueType && peVerifyCompatEnabled) - { - // due to array covariance getting a reference may throw ArrayTypeMismatch when element is not a struct, - // passing "readonly." prefix would prevent that, but it is unverifiable, so will make a copy in compat case - return false; - } - - return true; - - case BoundKind.PointerIndirectionOperator: - case BoundKind.RefValueOperator: - return true; - - case BoundKind.ThisReference: - var type = expression.Type; - if (type.IsReferenceType) - { - Debug.Assert(IsAnyReadOnly(addressKind), "`this` is readonly in classes"); - return true; - } - - if (!IsAnyReadOnly(addressKind) && containingSymbol is MethodSymbol { ContainingSymbol: NamedTypeSymbol, IsEffectivelyReadOnly: true }) - { - return false; - } - - return true; - - case BoundKind.ThrowExpression: - // vacuously this is true, we can take address of throw without temps - return true; - - case BoundKind.Parameter: - return IsAnyReadOnly(addressKind) || - ((BoundParameter)expression).ParameterSymbol.RefKind is not (RefKind.In or RefKind.RefReadOnlyParameter); - - case BoundKind.Local: - // locals have home unless they are byval stack locals or ref-readonly - // locals in a mutating call - var local = ((BoundLocal)expression).LocalSymbol; - return !((CodeGenerator.IsStackLocal(local, stackLocalsOpt) && local.RefKind == RefKind.None) || - (!IsAnyReadOnly(addressKind) && local.RefKind == RefKind.RefReadOnly)); - - case BoundKind.Call: - var methodRefKind = ((BoundCall)expression).Method.RefKind; - return methodRefKind == RefKind.Ref || - (IsAnyReadOnly(addressKind) && methodRefKind == RefKind.RefReadOnly); - - case BoundKind.Dup: - //NB: Dup represents locals that do not need IL slot - var dupRefKind = ((BoundDup)expression).RefKind; - return dupRefKind == RefKind.Ref || - (IsAnyReadOnly(addressKind) && dupRefKind == RefKind.RefReadOnly); - - case BoundKind.FieldAccess: - return FieldAccessHasHome((BoundFieldAccess)expression, addressKind, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt); - - case BoundKind.Sequence: - return HasHome(((BoundSequence)expression).Value, addressKind, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt); - - case BoundKind.AssignmentOperator: - var assignment = (BoundAssignmentOperator)expression; - if (!assignment.IsRef) - { - return false; - } - var lhsRefKind = assignment.Left.GetRefKind(); - return lhsRefKind == RefKind.Ref || - (IsAnyReadOnly(addressKind) && lhsRefKind is RefKind.RefReadOnly or RefKind.RefReadOnlyParameter); - - case BoundKind.ComplexConditionalReceiver: - Debug.Assert(HasHome( - ((BoundComplexConditionalReceiver)expression).ValueTypeReceiver, - addressKind, - containingSymbol, - peVerifyCompatEnabled, - stackLocalsOpt)); - Debug.Assert(HasHome( - ((BoundComplexConditionalReceiver)expression).ReferenceTypeReceiver, - addressKind, - containingSymbol, - peVerifyCompatEnabled, - stackLocalsOpt)); - goto case BoundKind.ConditionalReceiver; - - case BoundKind.ConditionalReceiver: - //ConditionalReceiver is a noop from Emit point of view. - it represents something that has already been pushed. - //We should never need a temp for it. - return true; - - case BoundKind.ConditionalOperator: - var conditional = (BoundConditionalOperator)expression; - - // only ref conditional may be referenced as a variable - if (!conditional.IsRef) - { - return false; - } - - // branch that has no home will need a temporary - // if both have no home, just say whole expression has no home - // so we could just use one temp for the whole thing - return HasHome(conditional.Consequence, addressKind, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt) - && HasHome(conditional.Alternative, addressKind, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt); - - default: - return false; - } - } - - /// - /// Special HasHome for fields. - /// A field has a readable home unless the field is a constant. - /// A ref readonly field doesn't have a writable home. - /// Other fields have a writable home unless the field is a readonly value - /// and is used outside of a constructor or init method. - /// - private static bool FieldAccessHasHome( - BoundFieldAccess fieldAccess, - AddressKind addressKind, - Symbol containingSymbol, - bool peVerifyCompatEnabled, - HashSet stackLocalsOpt) - { - Debug.Assert(containingSymbol is object); - - FieldSymbol field = fieldAccess.FieldSymbol; - - // const fields are literal values with no homes. (ex: decimal.Zero) - if (field.IsConst) - { - return false; - } - - if (field.RefKind is RefKind.Ref) - { - return true; - } - - // in readonly situations where ref to a copy is not allowed, consider fields as addressable - if (addressKind == AddressKind.ReadOnlyStrict) - { - return true; - } - - // ReadOnly references can always be taken unless we are in peverify compat mode - if (addressKind == AddressKind.ReadOnly && !peVerifyCompatEnabled) - { - return true; - } - - // Some field accesses must be values; values do not have homes. - if (fieldAccess.IsByValue) - { - return false; - } - - if (field.RefKind == RefKind.RefReadOnly) - { - return false; - } - - Debug.Assert(field.RefKind == RefKind.None); - - if (!field.IsReadOnly) - { - // in a case if we have a writeable struct field with a receiver that only has a readable home we would need to pass it via a temp. - // it would be advantageous to make a temp for the field, not for the outer struct, since the field is smaller and we can get to is by fetching references. - // NOTE: this would not be profitable if we have to satisfy verifier, since for verifiability - // we would not be able to dig for the inner field using references and the outer struct will have to be copied to a temp anyways. - if (!peVerifyCompatEnabled) - { - Debug.Assert(!IsAnyReadOnly(addressKind)); - - var receiver = fieldAccess.ReceiverOpt; - if (receiver?.Type.IsValueType == true) - { - // Check receiver: - // has writeable home -> return true - the whole chain has writeable home (also a more common case) - // has readable home -> return false - we need to copy the field - // otherwise -> return true - the copy will be made at higher level so the leaf field can have writeable home - - return HasHome(receiver, addressKind, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt) - || !HasHome(receiver, AddressKind.ReadOnly, containingSymbol, peVerifyCompatEnabled, stackLocalsOpt); - } - } - - return true; - } - - // while readonly fields have home it is not valid to refer to it when not constructing. - if (!TypeSymbol.Equals(field.ContainingType, containingSymbol.ContainingSymbol as NamedTypeSymbol, TypeCompareKind.AllIgnoreOptions)) - { - return false; - } - - if (field.IsStatic) - { - return containingSymbol is MethodSymbol { MethodKind: MethodKind.StaticConstructor } or FieldSymbol { IsStatic: true }; - } - else - { - return (containingSymbol is MethodSymbol { MethodKind: MethodKind.Constructor } or FieldSymbol { IsStatic: false } or MethodSymbol { IsInitOnly: true }) && - fieldAccess.ReceiverOpt.Kind == BoundKind.ThisReference; - } - } - } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 9e19db67c51ab..248134bfb8d59 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -1465,9 +1465,12 @@ private BoundExpression BindFieldExpression(FieldExpressionSyntax node, BindingD } } + // Field will be null when binding a field expression in a speculative + // semantic model when the property does not have a backing field. if (field is null) { - throw ExceptionUtilities.UnexpectedValue(ContainingMember()); + diagnostics.Add(ErrorCode.ERR_NoSuchMember, node, ContainingMember(), "field"); + return BadExpression(node); } var implicitReceiver = field.IsStatic ? null : ThisReference(node, field.ContainingType, wasCompilerGenerated: true); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 13230a166b2c1..7f62ca8b1feae 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -4377,13 +4377,13 @@ private void ValidateRefConditionalOperator(SyntaxNode node, BoundExpression tru var currentScope = _localScopeDepth; // val-escape must agree on both branches. - uint whenTrueEscape = GetValEscape(trueExpr, currentScope); - uint whenFalseEscape = GetValEscape(falseExpr, currentScope); + SafeContext whenTrueEscape = GetValEscape(trueExpr, currentScope); + SafeContext whenFalseEscape = GetValEscape(falseExpr, currentScope); if (whenTrueEscape != whenFalseEscape) { // ask the one with narrower escape, for the wider - hopefully the errors will make the violation easier to fix. - if (whenTrueEscape < whenFalseEscape) + if (!whenFalseEscape.IsConvertibleTo(whenTrueEscape)) CheckValEscape(falseExpr.Syntax, falseExpr, currentScope, whenTrueEscape, checkingReceiver: false, diagnostics: diagnostics); else CheckValEscape(trueExpr.Syntax, trueExpr, currentScope, whenFalseEscape, checkingReceiver: false, diagnostics: diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 0b4c03c795406..d3feb3d50ab4b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -239,7 +239,7 @@ private BoundStatement BindYieldReturnStatement(YieldStatementSyntax node, Bindi ? BadExpression(node).MakeCompilerGenerated() : BindValue(node.Expression, diagnostics, BindValueKind.RValue); - if (!argument.HasAnyErrors) + if (elementType is { } && node.Expression != null) { argument = GenerateConversionForAssignment(elementType, argument, diagnostics); } @@ -1482,11 +1482,13 @@ private BoundAssignmentOperator BindAssignment( bool hasErrors = op1.HasAnyErrors || op2.HasAnyErrors; - if (!op1.HasAnyErrors) + if (op1.Type is { } lhsType && !lhsType.IsErrorType()) { // Build bound conversion. The node might not be used if this is a dynamic conversion // but diagnostics should be reported anyways. - var conversion = GenerateConversionForAssignment(op1.Type, op2, diagnostics, isRef ? ConversionForAssignmentFlags.RefAssignment : ConversionForAssignmentFlags.None); + var conversion = GenerateConversionForAssignment(lhsType, op2, + hasErrors ? BindingDiagnosticBag.Discarded : diagnostics, + isRef ? ConversionForAssignmentFlags.RefAssignment : ConversionForAssignmentFlags.None); // If the result is a dynamic assignment operation (SetMember or SetIndex), // don't generate the boxing conversion to the dynamic type. @@ -1549,12 +1551,12 @@ private void ValidateAssignment( var leftEscape = GetRefEscape(op1, _localScopeDepth); var rightEscape = GetRefEscape(op2, _localScopeDepth); - if (leftEscape < rightEscape) + if (!rightEscape.IsConvertibleTo(leftEscape)) { var errorCode = (rightEscape, _inUnsafeRegion) switch { - (ReturnOnlyScope, false) => ErrorCode.ERR_RefAssignReturnOnly, - (ReturnOnlyScope, true) => ErrorCode.WRN_RefAssignReturnOnly, + ({ IsReturnOnly: true }, false) => ErrorCode.ERR_RefAssignReturnOnly, + ({ IsReturnOnly: true }, true) => ErrorCode.WRN_RefAssignReturnOnly, (_, false) => ErrorCode.ERR_RefAssignNarrower, (_, true) => ErrorCode.WRN_RefAssignNarrower }; @@ -1570,12 +1572,13 @@ private void ValidateAssignment( leftEscape = GetValEscape(op1, _localScopeDepth); rightEscape = GetValEscape(op2, _localScopeDepth); - Debug.Assert(leftEscape == rightEscape || op1.Type.IsRefLikeOrAllowsRefLikeType()); + Debug.Assert(leftEscape.Equals(rightEscape) || op1.Type.IsRefLikeOrAllowsRefLikeType()); - // We only check if the safe-to-escape of e2 is wider than the safe-to-escape of e1 here, - // we don't check for equality. The case where the safe-to-escape of e2 is narrower than - // e1 is handled in the if (op1.Type.IsRefLikeType) { ... } block later. - if (leftEscape > rightEscape) + // We only check if the left SafeContext is convertible to the right here + // in order to give a more useful diagnostic. + // Later on we check if right SafeContext is convertible to left, + // which effectively means these SafeContexts must be equal. + if (!leftEscape.IsConvertibleTo(rightEscape)) { Debug.Assert(op1.Kind != BoundKind.Parameter); // If the assert fails, add a corresponding test. @@ -3154,7 +3157,7 @@ internal BoundExpression CreateReturnConversion( diagnostics.Add(syntax, useSiteInfo); - if (!argument.HasAnyErrors) + if (!argument.HasAnyErrors || argument.Kind == BoundKind.UnboundLambda) { if (returnRefKind != RefKind.None) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index c35c4512565cd..b3c42874d46a0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -1373,14 +1373,32 @@ private NamedTypeSymbol ConstructNamedTypeUnlessTypeArgumentOmitted(SyntaxNode t { if (typeArgumentsSyntax.Any(SyntaxKind.OmittedTypeArgument)) { - // Note: lookup won't have reported this, since the arity was correct. - // CONSIDER: the text of this error message makes sense, but we might want to add a separate code. - Error(diagnostics, ErrorCode.ERR_BadArity, typeSyntax, type, MessageID.IDS_SK_TYPE.Localize(), typeArgumentsSyntax.Count); + if (this.IsInsideNameof) + { + // Inside a nameof an open-generic type is acceptable. Fall through and bind the remainder accordingly. + CheckFeatureAvailability(typeSyntax, MessageID.IDS_FeatureUnboundGenericTypesInNameof, diagnostics); + + // From the spec: + // + // Member lookup on an unbound type expression will be performed the same way as for a `this` + // expression within that type declaration. + // + // So we want to just return the originating type symbol as is (e.g. List in nameof(List<>)). + // This is distinctly different than how typeof(List<>) works, where it returns an unbound generic + // type. + } + else + { + // Note: lookup won't have reported this, since the arity was correct. + // CONSIDER: the text of this error message makes sense, but we might want to add a separate code. + + // If the syntax looks like an unbound generic type, then they probably wanted the definition. + // Give an error indicating that the syntax is incorrect and then use the definition. + // CONSIDER: we could construct an unbound generic type symbol, but that would probably be confusing + // outside a typeof. + Error(diagnostics, ErrorCode.ERR_BadArity, typeSyntax, type, MessageID.IDS_SK_TYPE.Localize(), typeArgumentsSyntax.Count); + } - // If the syntax looks like an unbound generic type, then they probably wanted the definition. - // Give an error indicating that the syntax is incorrect and then use the definition. - // CONSIDER: we could construct an unbound generic type symbol, but that would probably be confusing - // outside a typeof. return type; } else diff --git a/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs b/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs index 668cad30a97f3..acf4ba3dfcf59 100644 --- a/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/NameofBinder.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp @@ -19,19 +21,25 @@ namespace Microsoft.CodeAnalysis.CSharp /// internal sealed class NameofBinder : Binder { - private readonly SyntaxNode _nameofArgument; + private readonly ExpressionSyntax _nameofArgument; private readonly WithTypeParametersBinder? _withTypeParametersBinder; private readonly Binder? _withParametersBinder; private ThreeState _lazyIsNameofOperator; - internal NameofBinder(SyntaxNode nameofArgument, Binder next, WithTypeParametersBinder? withTypeParametersBinder, Binder? withParametersBinder) + private readonly Dictionary? _allowedMap; + + internal NameofBinder(ExpressionSyntax nameofArgument, Binder next, WithTypeParametersBinder? withTypeParametersBinder, Binder? withParametersBinder) : base(next) { _nameofArgument = nameofArgument; _withTypeParametersBinder = withTypeParametersBinder; _withParametersBinder = withParametersBinder; + OpenTypeVisitor.Visit(nameofArgument, out _allowedMap); } + protected override bool IsUnboundTypeAllowed(GenericNameSyntax syntax) + => _allowedMap != null && _allowedMap.TryGetValue(syntax, out bool allowed) && allowed; + private bool IsNameofOperator { get diff --git a/src/Compilers/CSharp/Portable/Binder/OpenTypeVisitor.cs b/src/Compilers/CSharp/Portable/Binder/OpenTypeVisitor.cs new file mode 100644 index 0000000000000..7654a5e40e9e1 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Binder/OpenTypeVisitor.cs @@ -0,0 +1,99 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp; + +internal partial class Binder +{ + /// + /// This visitor walks over a type expression looking for open types. + /// + /// Open types are allowed if an only if: + /// + /// There is no constructed generic type elsewhere in the visited syntax; and + /// The open type is not used as a type argument or array/pointer/nullable element type. + /// + /// + /// Open types can be used both in typeof(...) and nameof(...) expressions. + /// + protected sealed class OpenTypeVisitor : CSharpSyntaxVisitor + { + private Dictionary? _allowedMap; + private bool _seenConstructed; + + /// The argument to typeof. + /// + /// Keys are GenericNameSyntax nodes representing unbound generic types. + /// Values are false if the node should result in an error and true otherwise. + /// + public static void Visit(ExpressionSyntax typeSyntax, out Dictionary? allowedMap) + { + OpenTypeVisitor visitor = new OpenTypeVisitor(); + visitor.Visit(typeSyntax); + allowedMap = visitor._allowedMap; + } + + public override void VisitGenericName(GenericNameSyntax node) + { + SeparatedSyntaxList typeArguments = node.TypeArgumentList.Arguments; + if (node.IsUnboundGenericName) + { + _allowedMap ??= new Dictionary(); + _allowedMap[node] = !_seenConstructed; + } + else + { + _seenConstructed = true; + foreach (TypeSyntax arg in typeArguments) + { + Visit(arg); + } + } + } + + public override void VisitQualifiedName(QualifiedNameSyntax node) + { + bool seenConstructedBeforeRight = _seenConstructed; + + // Visit Right first because it's smaller (to make backtracking cheaper). + Visit(node.Right); + + bool seenConstructedBeforeLeft = _seenConstructed; + + Visit(node.Left); + + // If the first time we saw a constructed type was in Left, then we need to re-visit Right + if (!seenConstructedBeforeRight && !seenConstructedBeforeLeft && _seenConstructed) + { + Visit(node.Right); + } + } + + public override void VisitAliasQualifiedName(AliasQualifiedNameSyntax node) + { + Visit(node.Name); + } + + public override void VisitArrayType(ArrayTypeSyntax node) + { + _seenConstructed = true; + Visit(node.ElementType); + } + + public override void VisitPointerType(PointerTypeSyntax node) + { + _seenConstructed = true; + Visit(node.ElementType); + } + + public override void VisitNullableType(NullableTypeSyntax node) + { + _seenConstructed = true; + Visit(node.ElementType); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs b/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs index e754145df87b6..9e7ff5ea2f103 100644 --- a/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs +++ b/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs @@ -59,10 +59,10 @@ private static bool InUnsafeMethod(Symbol symbol) private readonly bool _useUpdatedEscapeRules; private readonly BindingDiagnosticBag _diagnostics; private bool _inUnsafeRegion; - private uint _localScopeDepth; - private Dictionary? _localEscapeScopes; - private Dictionary? _placeholderScopes; - private uint _patternInputValEscape; + private SafeContext _localScopeDepth; + private Dictionary? _localEscapeScopes; + private Dictionary? _placeholderScopes; + private SafeContext _patternInputValEscape; #if DEBUG private const int MaxTrackVisited = 100; // Avoid tracking if too many expressions. private HashSet? _visited = new HashSet(); @@ -73,8 +73,7 @@ private RefSafetyAnalysis( MethodSymbol symbol, bool inUnsafeRegion, bool useUpdatedEscapeRules, - BindingDiagnosticBag diagnostics, - Dictionary? localEscapeScopes = null) + BindingDiagnosticBag diagnostics) { _compilation = compilation; _symbol = symbol; @@ -84,8 +83,7 @@ private RefSafetyAnalysis( // _localScopeDepth is incremented at each block in the method, including the // outermost. To ensure that locals in the outermost block are considered at // the same depth as parameters, _localScopeDepth is initialized to one less. - _localScopeDepth = CurrentMethodScope - 1; - _localEscapeScopes = localEscapeScopes; + _localScopeDepth = SafeContext.CurrentMethod.Wider(); } private ref struct LocalScope @@ -97,10 +95,10 @@ public LocalScope(RefSafetyAnalysis analysis, ImmutableArray locals { _analysis = analysis; _locals = locals; - _analysis._localScopeDepth++; + _analysis._localScopeDepth = _analysis._localScopeDepth.Narrower(); foreach (var local in locals) { - _analysis.AddLocalScopes(local, refEscapeScope: _analysis._localScopeDepth, valEscapeScope: CallingMethodScope); + _analysis.AddLocalScopes(local, refEscapeScope: _analysis._localScopeDepth, valEscapeScope: SafeContext.CallingMethod); } } @@ -110,7 +108,7 @@ public void Dispose() { _analysis.RemoveLocalScopes(local); } - _analysis._localScopeDepth--; + _analysis._localScopeDepth = _analysis._localScopeDepth.Wider(); } } @@ -135,9 +133,9 @@ public void Dispose() private ref struct PatternInput { private readonly RefSafetyAnalysis _analysis; - private readonly uint _previousInputValEscape; + private readonly SafeContext _previousInputValEscape; - public PatternInput(RefSafetyAnalysis analysis, uint patternInputValEscape) + public PatternInput(RefSafetyAnalysis analysis, SafeContext patternInputValEscape) { _analysis = analysis; _previousInputValEscape = analysis._patternInputValEscape; @@ -153,9 +151,9 @@ public void Dispose() private ref struct PlaceholderRegion { private readonly RefSafetyAnalysis _analysis; - private readonly ArrayBuilder<(BoundValuePlaceholderBase, uint)> _placeholders; + private readonly ArrayBuilder<(BoundValuePlaceholderBase, SafeContext)> _placeholders; - public PlaceholderRegion(RefSafetyAnalysis analysis, ArrayBuilder<(BoundValuePlaceholderBase, uint)> placeholders) + public PlaceholderRegion(RefSafetyAnalysis analysis, ArrayBuilder<(BoundValuePlaceholderBase, SafeContext)> placeholders) { _analysis = analysis; _placeholders = placeholders; @@ -175,30 +173,30 @@ public void Dispose() } } - private (uint RefEscapeScope, uint ValEscapeScope) GetLocalScopes(LocalSymbol local) + private (SafeContext RefEscapeScope, SafeContext ValEscapeScope) GetLocalScopes(LocalSymbol local) { - Debug.Assert(_localEscapeScopes?.ContainsKey(local) == true); + Debug.Assert(_localEscapeScopes?.ContainsKey(local) == true || _symbol != local.ContainingSymbol); return _localEscapeScopes?.TryGetValue(local, out var scopes) == true ? scopes - : (CallingMethodScope, CallingMethodScope); + : (SafeContext.CurrentMethod, SafeContext.CallingMethod); } - private void SetLocalScopes(LocalSymbol local, uint refEscapeScope, uint valEscapeScope) + private void SetLocalScopes(LocalSymbol local, SafeContext refEscapeScope, SafeContext valEscapeScope) { Debug.Assert(_localEscapeScopes?.ContainsKey(local) == true); AddOrSetLocalScopes(local, refEscapeScope, valEscapeScope); } - private void AddPlaceholderScope(BoundValuePlaceholderBase placeholder, uint valEscapeScope) + private void AddPlaceholderScope(BoundValuePlaceholderBase placeholder, SafeContext valEscapeScope) { Debug.Assert(_placeholderScopes?.ContainsKey(placeholder) != true); // Consider not adding the placeholder to the dictionary if the escape scope is // CallingMethod, and simply fallback to that value in GetPlaceholderScope(). - _placeholderScopes ??= new Dictionary(); + _placeholderScopes ??= new Dictionary(); _placeholderScopes[placeholder] = valEscapeScope; } @@ -213,13 +211,13 @@ private void RemovePlaceholderScope(BoundValuePlaceholderBase placeholder) } #pragma warning restore IDE0060 - private uint GetPlaceholderScope(BoundValuePlaceholderBase placeholder) + private SafeContext GetPlaceholderScope(BoundValuePlaceholderBase placeholder) { Debug.Assert(_placeholderScopes?.ContainsKey(placeholder) == true); return _placeholderScopes?.TryGetValue(placeholder, out var scope) == true ? scope - : CallingMethodScope; + : SafeContext.CallingMethod; } #if DEBUG @@ -311,10 +309,7 @@ private void AssertVisited(BoundExpression expr) public override BoundNode? VisitLocalFunctionStatement(BoundLocalFunctionStatement node) { var localFunction = node.Symbol; - // https://github.com/dotnet/roslyn/issues/65353: We should not reuse _localEscapeScopes - // across nested local functions or lambdas because _localScopeDepth is reset when entering - // the function or lambda so the scopes across the methods are unrelated. - var analysis = new RefSafetyAnalysis(_compilation, localFunction, _inUnsafeRegion || localFunction.IsUnsafe, _useUpdatedEscapeRules, _diagnostics, _localEscapeScopes); + var analysis = new RefSafetyAnalysis(_compilation, localFunction, _inUnsafeRegion || localFunction.IsUnsafe, _useUpdatedEscapeRules, _diagnostics); analysis.Visit(node.BlockBody); analysis.Visit(node.ExpressionBody); return null; @@ -323,10 +318,7 @@ private void AssertVisited(BoundExpression expr) public override BoundNode? VisitLambda(BoundLambda node) { var lambda = node.Symbol; - // https://github.com/dotnet/roslyn/issues/65353: We should not reuse _localEscapeScopes - // across nested local functions or lambdas because _localScopeDepth is reset when entering - // the function or lambda so the scopes across the methods are unrelated. - var analysis = new RefSafetyAnalysis(_compilation, lambda, _inUnsafeRegion, _useUpdatedEscapeRules, _diagnostics, _localEscapeScopes); + var analysis = new RefSafetyAnalysis(_compilation, lambda, _inUnsafeRegion, _useUpdatedEscapeRules, _diagnostics); analysis.Visit(node.Body); return null; } @@ -351,10 +343,10 @@ private void AssertVisited(BoundExpression expr) this.Visit(node.DeclarationsOpt); this.Visit(node.ExpressionOpt); - var placeholders = ArrayBuilder<(BoundValuePlaceholderBase, uint)>.GetInstance(); + var placeholders = ArrayBuilder<(BoundValuePlaceholderBase, SafeContext)>.GetInstance(); if (node.AwaitOpt is { } awaitableInfo) { - uint valEscapeScope = node.ExpressionOpt is { } expr + SafeContext valEscapeScope = node.ExpressionOpt is { } expr ? GetValEscape(expr, _localScopeDepth) : _localScopeDepth; GetAwaitableInstancePlaceholders(placeholders, awaitableInfo, valEscapeScope); @@ -368,7 +360,7 @@ private void AssertVisited(BoundExpression expr) public override BoundNode? VisitUsingLocalDeclarations(BoundUsingLocalDeclarations node) { - var placeholders = ArrayBuilder<(BoundValuePlaceholderBase, uint)>.GetInstance(); + var placeholders = ArrayBuilder<(BoundValuePlaceholderBase, SafeContext)>.GetInstance(); if (node.AwaitOpt is { } awaitableInfo) { GetAwaitableInstancePlaceholders(placeholders, awaitableInfo, _localScopeDepth); @@ -434,14 +426,13 @@ private void AssertVisited(BoundExpression expr) public override BoundNode? VisitLocal(BoundLocal node) { - // _localEscapeScopes may be null for locals in top-level statements. Debug.Assert(_localEscapeScopes?.ContainsKey(node.LocalSymbol) == true || - (node.LocalSymbol.ContainingSymbol is SynthesizedSimpleProgramEntryPointSymbol entryPoint && _symbol != entryPoint)); + _symbol != node.LocalSymbol.ContainingSymbol); return base.VisitLocal(node); } - private void AddLocalScopes(LocalSymbol local, uint refEscapeScope, uint valEscapeScope) + private void AddLocalScopes(LocalSymbol local, SafeContext refEscapeScope, SafeContext valEscapeScope) { // From https://github.com/dotnet/csharplang/blob/main/csharp-11.0/proposals/low-level-struct-improvements.md: // @@ -457,10 +448,10 @@ private void AddLocalScopes(LocalSymbol local, uint refEscapeScope, uint valEsca { refEscapeScope = scopedModifier == ScopedKind.ScopedRef ? _localScopeDepth : - CurrentMethodScope; + SafeContext.CurrentMethod; valEscapeScope = scopedModifier == ScopedKind.ScopedValue ? _localScopeDepth : - CallingMethodScope; + SafeContext.CallingMethod; } Debug.Assert(_localEscapeScopes?.ContainsKey(local) != true); @@ -468,9 +459,9 @@ private void AddLocalScopes(LocalSymbol local, uint refEscapeScope, uint valEsca AddOrSetLocalScopes(local, refEscapeScope, valEscapeScope); } - private void AddOrSetLocalScopes(LocalSymbol local, uint refEscapeScope, uint valEscapeScope) + private void AddOrSetLocalScopes(LocalSymbol local, SafeContext refEscapeScope, SafeContext valEscapeScope) { - _localEscapeScopes ??= new Dictionary(); + _localEscapeScopes ??= new Dictionary(); _localEscapeScopes[local] = (refEscapeScope, valEscapeScope); } @@ -491,15 +482,15 @@ private void RemoveLocalScopes(LocalSymbol local) if (node.InitializerOpt is { } initializer) { var localSymbol = (SourceLocalSymbol)node.LocalSymbol; - (uint refEscapeScope, uint valEscapeScope) = GetLocalScopes(localSymbol); + (SafeContext refEscapeScope, SafeContext valEscapeScope) = GetLocalScopes(localSymbol); if (_useUpdatedEscapeRules && localSymbol.Scope != ScopedKind.None) { - // If the local has a scoped modifier, then the lifetime is not inferred from + // If the local has a scoped modifier, then the SafeContext is not inferred from // the initializer. Validate the escape values for the initializer instead. Debug.Assert(localSymbol.RefKind == RefKind.None || - refEscapeScope >= GetRefEscape(initializer, _localScopeDepth)); + GetRefEscape(initializer, _localScopeDepth).IsConvertibleTo(refEscapeScope)); if (node.DeclaredTypeOpt?.Type.IsRefLikeOrAllowsRefLikeType() == true) { @@ -529,7 +520,7 @@ private void RemoveLocalScopes(LocalSymbol local) base.VisitReturnStatement(node); if (node.ExpressionOpt is { Type: { } } expr) { - ValidateEscape(expr, ReturnOnlyScope, node.RefKind != RefKind.None, _diagnostics); + ValidateEscape(expr, SafeContext.ReturnOnly, node.RefKind != RefKind.None, _diagnostics); } return null; } @@ -539,7 +530,7 @@ private void RemoveLocalScopes(LocalSymbol local) base.VisitYieldReturnStatement(node); if (node.Expression is { Type: { } } expr) { - ValidateEscape(expr, ReturnOnlyScope, isByRef: false, _diagnostics); + ValidateEscape(expr, SafeContext.ReturnOnly, isByRef: false, _diagnostics); } return null; } @@ -576,12 +567,12 @@ private void RemoveLocalScopes(LocalSymbol local) using var _ = new PatternInput(this, getDeclarationValEscape(node.DeclaredType, _patternInputValEscape)); return base.VisitDeclarationPattern(node); - static uint getDeclarationValEscape(BoundTypeExpression typeExpression, uint valEscape) + static SafeContext getDeclarationValEscape(BoundTypeExpression typeExpression, SafeContext valEscape) { // https://github.com/dotnet/roslyn/issues/73551: // We do not have a test that demonstrates the statement below makes a difference - // for ref like types. If 'CallingMethodScope' is always returned, not a single test fails. - return typeExpression.Type.IsRefLikeOrAllowsRefLikeType() ? valEscape : CallingMethodScope; + // for ref like types. If 'SafeContext.CallingMethod' is always returned, not a single test fails. + return typeExpression.Type.IsRefLikeOrAllowsRefLikeType() ? valEscape : SafeContext.CallingMethod; } } @@ -602,11 +593,11 @@ static uint getDeclarationValEscape(BoundTypeExpression typeExpression, uint val using var _ = new PatternInput(this, getPositionalValEscape(node.Symbol, _patternInputValEscape)); return base.VisitPositionalSubpattern(node); - static uint getPositionalValEscape(Symbol? symbol, uint valEscape) + static SafeContext getPositionalValEscape(Symbol? symbol, SafeContext valEscape) { return symbol is null ? valEscape - : symbol.GetTypeOrReturnType().IsRefLikeOrAllowsRefLikeType() ? valEscape : CallingMethodScope; + : symbol.GetTypeOrReturnType().IsRefLikeOrAllowsRefLikeType() ? valEscape : SafeContext.CallingMethod; } } @@ -615,11 +606,11 @@ static uint getPositionalValEscape(Symbol? symbol, uint valEscape) using var _ = new PatternInput(this, getMemberValEscape(node.Member, _patternInputValEscape)); return base.VisitPropertySubpattern(node); - static uint getMemberValEscape(BoundPropertySubpatternMember? member, uint valEscape) + static SafeContext getMemberValEscape(BoundPropertySubpatternMember? member, SafeContext valEscape) { if (member is null) return valEscape; valEscape = getMemberValEscape(member.Receiver, valEscape); - return member.Type.IsRefLikeOrAllowsRefLikeType() ? valEscape : CallingMethodScope; + return member.Type.IsRefLikeOrAllowsRefLikeType() ? valEscape : SafeContext.CallingMethod; } } @@ -649,7 +640,7 @@ private void VisitArgumentsAndGetArgumentPlaceholders(BoundExpression? receiverO if (arg is BoundConversion { ConversionKind: ConversionKind.InterpolatedStringHandler, Operand: BoundInterpolatedString or BoundBinaryOperator } conversion) { var interpolationData = conversion.Operand.GetInterpolatedStringHandlerData(); - var placeholders = ArrayBuilder<(BoundValuePlaceholderBase, uint)>.GetInstance(); + var placeholders = ArrayBuilder<(BoundValuePlaceholderBase, SafeContext)>.GetInstance(); GetInterpolatedStringPlaceholders(placeholders, interpolationData, receiverOpt, i, arguments); _ = new PlaceholderRegion(this, placeholders); } @@ -680,7 +671,7 @@ protected override void VisitArguments(BoundCall node) } private void GetInterpolatedStringPlaceholders( - ArrayBuilder<(BoundValuePlaceholderBase, uint)> placeholders, + ArrayBuilder<(BoundValuePlaceholderBase, SafeContext)> placeholders, in InterpolatedStringHandlerData interpolationData, BoundExpression? receiver, int nArgumentsVisited, @@ -691,7 +682,7 @@ private void GetInterpolatedStringPlaceholders( foreach (var placeholder in interpolationData.ArgumentPlaceholders) { - uint valEscapeScope; + SafeContext valEscapeScope; int argIndex = placeholder.ArgumentIndex; switch (argIndex) { @@ -699,7 +690,7 @@ private void GetInterpolatedStringPlaceholders( Debug.Assert(receiver != null); if (receiver is null) { - valEscapeScope = CallingMethodScope; + valEscapeScope = SafeContext.CallingMethod; } else { @@ -721,7 +712,7 @@ private void GetInterpolatedStringPlaceholders( else { // Error condition, see ERR_InterpolatedStringHandlerArgumentLocatedAfterInterpolatedString. - valEscapeScope = CallingMethodScope; // Consider skipping this placeholder entirely since CallingMethodScope is the fallback in GetPlaceholderScope(). + valEscapeScope = SafeContext.CallingMethod; // Consider skipping this placeholder entirely since SafeContext.CallingMethod is the fallback in GetPlaceholderScope(). } break; default: @@ -821,7 +812,7 @@ private void VisitObjectCreationExpressionBase(BoundObjectCreationExpressionBase continue; } - if (escapeFrom > GetValEscape(argument, _localScopeDepth)) + if (!escapeFrom.IsConvertibleTo(GetValEscape(argument, _localScopeDepth))) { Error(_diagnostics, ErrorCode.ERR_CallArgMixing, argument.Syntax, constructor, parameter.Name); } @@ -890,14 +881,14 @@ private void VisitObjectCreationExpressionBase(BoundObjectCreationExpressionBase public override BoundNode? VisitAwaitExpression(BoundAwaitExpression node) { this.Visit(node.Expression); - var placeholders = ArrayBuilder<(BoundValuePlaceholderBase, uint)>.GetInstance(); + var placeholders = ArrayBuilder<(BoundValuePlaceholderBase, SafeContext)>.GetInstance(); GetAwaitableInstancePlaceholders(placeholders, node.AwaitableInfo, GetValEscape(node.Expression, _localScopeDepth)); using var _ = new PlaceholderRegion(this, placeholders); this.Visit(node.AwaitableInfo); return null; } - private void GetAwaitableInstancePlaceholders(ArrayBuilder<(BoundValuePlaceholderBase, uint)> placeholders, BoundAwaitableInfo awaitableInfo, uint valEscapeScope) + private void GetAwaitableInstancePlaceholders(ArrayBuilder<(BoundValuePlaceholderBase, SafeContext)> placeholders, BoundAwaitableInfo awaitableInfo, SafeContext valEscapeScope) { if (awaitableInfo.AwaitableInstancePlaceholder is { } placeholder) { @@ -951,7 +942,7 @@ private void VisitDeconstructionArguments(ArrayBuilder v return; } - var placeholders = ArrayBuilder<(BoundValuePlaceholderBase, uint)>.GetInstance(); + var placeholders = ArrayBuilder<(BoundValuePlaceholderBase, SafeContext)>.GetInstance(); placeholders.Add((conversion.DeconstructionInfo.InputPlaceholder, GetValEscape(right, _localScopeDepth))); var parameters = deconstructMethod.Parameters; @@ -964,7 +955,7 @@ private void VisitDeconstructionArguments(ArrayBuilder v var variable = variables[i]; var nestedVariables = variable.NestedVariables; var arg = (BoundDeconstructValuePlaceholder)invocation.Arguments[i + offset]; - uint valEscape = nestedVariables is null + SafeContext valEscape = nestedVariables is null ? GetValEscape(variable.Expression, _localScopeDepth) : _localScopeDepth; placeholders.Add((arg, valEscape)); @@ -1000,10 +991,10 @@ private void VisitDeconstructionArguments(ArrayBuilder v private readonly struct DeconstructionVariable { internal readonly BoundExpression Expression; - internal readonly uint ValEscape; + internal readonly SafeContext ValEscape; internal readonly ArrayBuilder? NestedVariables; - internal DeconstructionVariable(BoundExpression expression, uint valEscape, ArrayBuilder? nestedVariables) + internal DeconstructionVariable(BoundExpression expression, SafeContext valEscape, ArrayBuilder? nestedVariables) { Expression = expression; ValEscape = valEscape; @@ -1024,7 +1015,7 @@ private ArrayBuilder GetDeconstructionAssignmentVariable DeconstructionVariable getDeconstructionAssignmentVariable(BoundExpression expr) { return expr is BoundTupleExpression tuple - ? new DeconstructionVariable(expr, valEscape: uint.MaxValue, GetDeconstructionAssignmentVariables(tuple)) + ? new DeconstructionVariable(expr, valEscape: SafeContext.Empty, GetDeconstructionAssignmentVariables(tuple)) : new DeconstructionVariable(expr, GetValEscape(expr, _localScopeDepth), null); } } @@ -1051,7 +1042,7 @@ private static ImmutableArray GetDeconstructionRightParts(Bound public override BoundNode? VisitForEachStatement(BoundForEachStatement node) { this.Visit(node.Expression); - uint collectionEscape; + SafeContext collectionEscape; if (node.EnumeratorInfoOpt is { InlineArraySpanType: not WellKnownType.Unknown and var spanType, InlineArrayUsedAsValue: false }) { @@ -1087,7 +1078,7 @@ private static ImmutableArray GetDeconstructionRightParts(Bound AddLocalScopes(local, refEscapeScope: local.RefKind == RefKind.None ? _localScopeDepth : collectionEscape, valEscapeScope: collectionEscape); } - var placeholders = ArrayBuilder<(BoundValuePlaceholderBase, uint)>.GetInstance(); + var placeholders = ArrayBuilder<(BoundValuePlaceholderBase, SafeContext)>.GetInstance(); if (node.DeconstructionOpt?.TargetPlaceholder is { } targetPlaceholder) { placeholders.Add((targetPlaceholder, collectionEscape)); diff --git a/src/Compilers/CSharp/Portable/Binder/SafeContext.cs b/src/Compilers/CSharp/Portable/Binder/SafeContext.cs new file mode 100644 index 0000000000000..cb8c426c07a03 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Binder/SafeContext.cs @@ -0,0 +1,132 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp +{ + /// + /// A representation of the program region in which the *referent* of a `ref` is *live*. + /// Limited to what is expressible in C#. + /// See also: + /// - https://github.com/dotnet/csharplang/blob/main/proposals/csharp-11.0/low-level-struct-improvements.md#detailed-design + /// - https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/variables.md#972-ref-safe-contexts + /// - https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/structs.md#16412-safe-context-constraint + /// + /// + /// - A *referent* is the variable being referenced by a `ref`. + /// - Informally, a variable is *live* if it has storage allocated for it (either on heap or stack). + /// - In this design, all SafeContexts have a known relationship to all other SafeContexts. + /// + internal readonly struct SafeContext + { + private const uint CallingMethodRaw = 0; + private const uint ReturnOnlyRaw = 1; + private const uint CurrentMethodRaw = 2; + + /// + /// For the purpose of escape verification we operate with the depth of local scopes. + /// The depth is a uint, with smaller number representing shallower/wider scopes. + /// Since sibling scopes do not intersect and a value cannot escape from one to another without + /// escaping to a wider scope, we can use simple depth numbering without ambiguity. + /// + private readonly uint _value; + private SafeContext(uint value) => _value = value; + + /// + /// The "calling method" scope that is outside of the containing method/lambda. + /// If something can escape to this scope, it can escape to any scope in a given method through a ref parameter or return. + /// + public static readonly SafeContext CallingMethod = new SafeContext(CallingMethodRaw); + + /// + /// The "return-only" scope that is outside of the containing method/lambda. + /// If something can escape to this scope, it can escape to any scope in a given method or can be returned, but it can't escape through a ref parameter. + /// + public static readonly SafeContext ReturnOnly = new SafeContext(ReturnOnlyRaw); + + /// + /// The "current method" scope that is just inside the containing method/lambda. + /// If something can escape to this scope, it can escape to any scope in a given method, but cannot be returned. + /// + public static readonly SafeContext CurrentMethod = new SafeContext(CurrentMethodRaw); + + /// + /// Gets a SafeContext which is "empty". i.e. which refers to a variable whose storage is never allocated. + /// + public static readonly SafeContext Empty = new SafeContext(uint.MaxValue); + + /// + /// Gets a SafeContext which is narrower than the given SafeContext. + /// Used to "enter" a nested local scope. + /// + public SafeContext Narrower() + { + Debug.Assert(_value >= ReturnOnlyRaw); + return new SafeContext(_value + 1); + } + + /// + /// Gets a SafeContext which is wider than the given SafeContext. + /// Used to "exit" a nested local scope. + /// + public SafeContext Wider() + { + Debug.Assert(_value >= CurrentMethodRaw); + return new SafeContext(_value - 1); + } + + public bool IsCallingMethod => _value == CallingMethodRaw; + public bool IsReturnOnly => _value == ReturnOnlyRaw; + public bool IsReturnable => _value is CallingMethodRaw or ReturnOnlyRaw; + + /// Returns true if a 'ref' with this SafeContext can be converted to the 'other' SafeContext. Otherwise, returns false. + /// Generally, a wider SafeContext is convertible to a narrower SafeContext. + public bool IsConvertibleTo(SafeContext other) + => this._value <= other._value; + + /// + /// Returns the narrower of two SafeContexts. + /// + /// + /// In other words, this method returns the widest SafeContext which 'this' and 'other' are both convertible to. + /// If in future we added the concept of unrelated SafeContexts (e.g. to implement 'ref scoped'), this method would perhaps return a Nullable, + /// for the case that no SafeContext exists which both input SafeContexts are convertible to. + /// + public SafeContext Intersect(SafeContext other) + => this.IsConvertibleTo(other) ? other : this; + + /// + /// Returns the wider of two SafeContexts. + /// + /// In other words, this method returns the narrowest SafeContext which can be converted to both 'this' and 'other'. + public SafeContext Union(SafeContext other) + => this.IsConvertibleTo(other) ? this : other; + + /// Returns true if this SafeContext is the same as 'other' (i.e. for invariant nested conversion). + public bool Equals(SafeContext other) + => this._value == other._value; + + public override bool Equals(object? obj) + => obj is SafeContext other && this.Equals(other); + + public override int GetHashCode() + => unchecked((int)_value); + + public static bool operator ==(SafeContext lhs, SafeContext rhs) + => lhs._value == rhs._value; + + public static bool operator !=(SafeContext lhs, SafeContext rhs) + => lhs._value != rhs._value; + + public override string ToString() + => _value switch + { + CallingMethodRaw => "SafeContext", + ReturnOnlyRaw => "SafeContext", + CurrentMethodRaw => "SafeContext", + _ => $"SafeContext<{_value}>" + }; + } +} diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs index d96fd06e7a0d4..35d044d2b9714 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorAnalysisResult.cs @@ -36,6 +36,7 @@ public bool HasValue get { return this.Kind != OperatorAnalysisResultKind.Undefined; } } + bool IMemberResolutionResultWithPriority.IsApplicable => IsValid; MethodSymbol IMemberResolutionResultWithPriority.MemberWithPriority => Signature.Method; public override bool Equals(object obj) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorAnalysisResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorAnalysisResult.cs index 46773fa51a037..c5ac83e989313 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorAnalysisResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorAnalysisResult.cs @@ -31,6 +31,7 @@ public bool HasValue get { return this.Kind != OperatorAnalysisResultKind.Undefined; } } + bool IMemberResolutionResultWithPriority.IsApplicable => IsValid; MethodSymbol IMemberResolutionResultWithPriority.MemberWithPriority => Signature.Method; public static UnaryOperatorAnalysisResult Applicable(UnaryOperatorSignature signature, Conversion conversion) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/IMemberResolutionResultWithPriority.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/IMemberResolutionResultWithPriority.cs index 747c26b2c5228..6c2176157c6d2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/IMemberResolutionResultWithPriority.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/IMemberResolutionResultWithPriority.cs @@ -11,4 +11,5 @@ namespace Microsoft.CodeAnalysis.CSharp; internal interface IMemberResolutionResultWithPriority where TMember : Symbol { TMember? MemberWithPriority { get; } + bool IsApplicable { get; } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index 48d5f0de526f6..1c8f97db8f811 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -1860,12 +1860,17 @@ private void RemoveLowerPriorityMembers(ArrayBuilder bool removedMembers = false; var resultsByContainingType = PooledDictionary>.GetInstance(); + var inapplicableMembers = ArrayBuilder.GetInstance(); foreach (var result in results) { - if (result.MemberWithPriority is null) + Debug.Assert(result.MemberWithPriority is not null); + + // We don't filter out inapplicable members here, as we want to keep them in the list for diagnostics + // However, we don't want to take them into account for the priority filtering + if (!result.IsApplicable) { - // Can happen for things like built-in binary operators + inapplicableMembers.Add(result); continue; } @@ -1900,6 +1905,7 @@ private void RemoveLowerPriorityMembers(ArrayBuilder { // No changes, so we can just return resultsByContainingType.Free(); + inapplicableMembers.Free(); return; } @@ -1908,7 +1914,9 @@ private void RemoveLowerPriorityMembers(ArrayBuilder { results.AddRange(resultsForType); } + results.AddRange(inapplicableMembers); resultsByContainingType.Free(); + inapplicableMembers.Free(); } private void RemoveWorseMembers(ArrayBuilder> results, AnalyzedArguments arguments, ref CompoundUseSiteInfo useSiteInfo) @@ -2972,6 +2980,19 @@ private BetterResult BetterConversionFromExpression(BoundExpression node, TypeSy switch ((conv1.Kind, conv2.Kind)) { case (ConversionKind.ImplicitSpan, ConversionKind.ImplicitSpan): + // If the expression is of an array type, prefer ReadOnlySpan over Span (to avoid ArrayTypeMismatchExceptions). + if (node.Type is ArrayTypeSymbol) + { + if (t1.IsReadOnlySpan() && t2.IsSpan()) + { + return BetterResult.Left; + } + + if (t1.IsSpan() && t2.IsReadOnlySpan()) + { + return BetterResult.Right; + } + } break; case (_, ConversionKind.ImplicitSpan): return BetterResult.Right; diff --git a/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs b/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs index 279fbd58e052c..7f0c3d231363e 100644 --- a/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/TypeofBinder.cs @@ -36,92 +36,5 @@ protected override bool IsUnboundTypeAllowed(GenericNameSyntax syntax) bool allowed; return _allowedMap != null && _allowedMap.TryGetValue(syntax, out allowed) && allowed; } - - /// - /// This visitor walks over a type expression looking for open types. - /// Open types are allowed if an only if: - /// 1) There is no constructed generic type elsewhere in the visited syntax; and - /// 2) The open type is not used as a type argument or array/pointer/nullable - /// element type. - /// - private class OpenTypeVisitor : CSharpSyntaxVisitor - { - private Dictionary _allowedMap; - private bool _seenConstructed; - - /// The argument to typeof. - /// - /// Keys are GenericNameSyntax nodes representing unbound generic types. - /// Values are false if the node should result in an error and true otherwise. - /// - public static void Visit(ExpressionSyntax typeSyntax, out Dictionary allowedMap) - { - OpenTypeVisitor visitor = new OpenTypeVisitor(); - visitor.Visit(typeSyntax); - allowedMap = visitor._allowedMap; - } - - public override void VisitGenericName(GenericNameSyntax node) - { - SeparatedSyntaxList typeArguments = node.TypeArgumentList.Arguments; - if (node.IsUnboundGenericName) - { - if (_allowedMap == null) - { - _allowedMap = new Dictionary(); - } - _allowedMap[node] = !_seenConstructed; - } - else - { - _seenConstructed = true; - foreach (TypeSyntax arg in typeArguments) - { - Visit(arg); - } - } - } - - public override void VisitQualifiedName(QualifiedNameSyntax node) - { - bool seenConstructedBeforeRight = _seenConstructed; - - // Visit Right first because it's smaller (to make backtracking cheaper). - Visit(node.Right); - - bool seenConstructedBeforeLeft = _seenConstructed; - - Visit(node.Left); - - // If the first time we saw a constructed type was in Left, then we need to re-visit Right - if (!seenConstructedBeforeRight && !seenConstructedBeforeLeft && _seenConstructed) - { - Visit(node.Right); - } - } - - public override void VisitAliasQualifiedName(AliasQualifiedNameSyntax node) - { - Visit(node.Name); - } - - public override void VisitArrayType(ArrayTypeSyntax node) - { - _seenConstructed = true; - Visit(node.ElementType); - } - - public override void VisitPointerType(PointerTypeSyntax node) - { - _seenConstructed = true; - Visit(node.ElementType); - } - - public override void VisitNullableType(NullableTypeSyntax node) - { - _seenConstructed = true; - Visit(node.ElementType); - } - } } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 28e5f8a9cc8c0..b68f25b9d7efe 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -39,6 +39,7 @@ + @@ -108,7 +109,7 @@ --> - + true - Generated + $(MSBuildThisFileDirectory)\Generated diff --git a/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs b/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs index ef1f8ebe9cdf7..7c6641d1fd3a1 100644 --- a/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs @@ -31,15 +31,10 @@ public void ReInitialize(DirectiveStack context) public CSharpSyntaxNode ParseDirective( bool isActive, bool endIsActive, - bool isAfterFirstTokenInFile, - bool isAfterNonWhitespaceOnLine) + bool isFollowingToken) { var hashPosition = lexer.TextWindow.Position; var hash = this.EatToken(SyntaxKind.HashToken, false); - if (isAfterNonWhitespaceOnLine) - { - hash = this.AddError(hash, ErrorCode.ERR_BadDirectivePlacement); - } // The behavior of these directives when isActive is false is somewhat complicated. // The key functions in the native compiler are ScanPreprocessorIfSection and @@ -81,7 +76,7 @@ public CSharpSyntaxNode ParseDirective( case SyntaxKind.DefineKeyword: case SyntaxKind.UndefKeyword: - result = this.ParseDefineOrUndefDirective(hash, this.EatContextualToken(contextualKind), isActive, isAfterFirstTokenInFile && !isAfterNonWhitespaceOnLine); + result = this.ParseDefineOrUndefDirective(hash, this.EatContextualToken(contextualKind), isActive, isFollowingToken); break; case SyntaxKind.ErrorKeyword: @@ -101,11 +96,11 @@ public CSharpSyntaxNode ParseDirective( break; case SyntaxKind.ReferenceKeyword: - result = this.ParseReferenceDirective(hash, this.EatContextualToken(contextualKind), isActive, isAfterFirstTokenInFile && !isAfterNonWhitespaceOnLine); + result = this.ParseReferenceDirective(hash, this.EatContextualToken(contextualKind), isActive, isFollowingToken); break; case SyntaxKind.LoadKeyword: - result = this.ParseLoadDirective(hash, this.EatContextualToken(contextualKind), isActive, isAfterFirstTokenInFile && !isAfterNonWhitespaceOnLine); + result = this.ParseLoadDirective(hash, this.EatContextualToken(contextualKind), isActive, isFollowingToken); break; case SyntaxKind.NullableKeyword: @@ -121,16 +116,13 @@ public CSharpSyntaxNode ParseDirective( { var id = this.EatToken(SyntaxKind.IdentifierToken, false); var end = this.ParseEndOfDirective(ignoreErrors: true); - if (!isAfterNonWhitespaceOnLine) + if (!id.IsMissing) { - if (!id.IsMissing) - { - id = this.AddError(id, ErrorCode.ERR_PPDirectiveExpected); - } - else - { - hash = this.AddError(hash, ErrorCode.ERR_PPDirectiveExpected); - } + id = this.AddError(id, ErrorCode.ERR_PPDirectiveExpected); + } + else + { + hash = this.AddError(hash, ErrorCode.ERR_PPDirectiveExpected); } result = SyntaxFactory.BadDirectiveTrivia(hash, id, end, isActive); diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index d96514ff2769c..8ed96d2f9c71a 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -15,6 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax { + using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Syntax.InternalSyntax; internal sealed partial class LanguageParser : SyntaxParser @@ -5029,6 +5030,28 @@ private void ParseVariableDeclarators( } else if (this.CurrentToken.Kind == SyntaxKind.CommaToken) { + // If we see `for (int i = 0, j < ...` then we do not want to consume j as the next declarator. + // + // Legal forms here are `for (int i = 0, j; ...` or `for (int i = 0, j = ...` or `for (int i = 0, j)`. + // + // We also accept: `for (int i = 0, ;` as that's likely an intermediary state prior to writing the next + // variable. + // + // Anything else we'll treat as as more likely to be the following conditional. + + if (flags.HasFlag(VariableFlags.ForStatement) && this.PeekToken(1).Kind != SyntaxKind.SemicolonToken) + { + // `int i = 0, ...` where what follows is not an identifier. Don't treat this as the start of a + // second variable. + if (!IsTrueIdentifier(this.PeekToken(1))) + break; + + // `int i = 0, j ...` where what follows is not something that continues a variable declaration. + // In this case, treat that `j` as the start of the condition expression instead. + if (this.PeekToken(2).Kind is not (SyntaxKind.SemicolonToken or SyntaxKind.EqualsToken or SyntaxKind.CloseParenToken)) + break; + } + variables.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); variables.Add( this.ParseVariableDeclarator( @@ -5062,9 +5085,11 @@ private PostSkipAction SkipBadVariableListTokens(SeparatedSyntaxListBuilder` or + // `Dictionary` We still want to think of these as generics, just with missing type-arguments, vs + // some invalid tree-expression that we would otherwise form. + if (this.CurrentToken.Kind == SyntaxKind.CommaToken) + { + lastScannedType = default; + continue; + } + lastScannedType = this.ScanType(out _); switch (lastScannedType) { @@ -8804,7 +8838,7 @@ private FixedStatementSyntax ParseFixedStatement(SyntaxList var saveTerm = _termState; _termState |= TerminatorState.IsEndOfFixedStatement; - var decl = ParseParenthesizedVariableDeclaration(); + var decl = ParseParenthesizedVariableDeclaration(VariableFlags.None, scopedKeyword: null); _termState = saveTerm; return _syntaxFactory.FixedStatement( @@ -9121,22 +9155,45 @@ private ForStatementSyntax ParseForStatement(SyntaxList att { Debug.Assert(this.CurrentToken.Kind == SyntaxKind.ForKeyword); + var saveTerm = _termState; + _termState |= TerminatorState.IsEndOfForStatementArgument; + var forToken = this.EatToken(SyntaxKind.ForKeyword); var openParen = this.EatToken(SyntaxKind.OpenParenToken); + var (variableDeclaration, initializers) = eatVariableDeclarationOrInitializers(); - var saveTerm = _termState; - _termState |= TerminatorState.IsEndOfForStatementArgument; + // Pulled out as we need to track this when parsing incrementors to place skipped tokens. + SyntaxToken secondSemicolonToken; + var forStatement = _syntaxFactory.ForStatement( + attributes, + forToken, + openParen, + variableDeclaration, + initializers, + firstSemicolonToken: eatCommaOrSemicolon(), + condition: this.CurrentToken.Kind is not SyntaxKind.SemicolonToken and not SyntaxKind.CommaToken + ? this.ParseExpressionCore() + : null, + secondSemicolonToken = eatCommaOrSemicolon(), + // Do allow semicolons (with diagnostics) in the incrementors list. This allows us to consume + // accidental extra incrementors that should have been separated by commas. + incrementors: this.CurrentToken.Kind != SyntaxKind.CloseParenToken + ? parseForStatementExpressionList(ref secondSemicolonToken, allowSemicolonAsSeparator: true) + : default, + eatUnexpectedTokensAndCloseParenToken(), + ParseEmbeddedStatement()); - var resetPoint = this.GetResetPoint(); - var initializers = default(SeparatedSyntaxList); - var incrementors = default(SeparatedSyntaxList); - try + _termState = saveTerm; + + return forStatement; + + (VariableDeclarationSyntax variableDeclaration, SeparatedSyntaxList initializers) eatVariableDeclarationOrInitializers() { + using var resetPoint = this.GetDisposableResetPoint(resetOnDispose: false); + // Here can be either a declaration or an expression statement list. Scan // for a declaration first. - VariableDeclarationSyntax decl = null; bool isDeclaration = false; - bool haveScopedKeyword = false; if (this.CurrentToken.ContextualKind == SyntaxKind.ScopedKeyword) { @@ -9148,10 +9205,8 @@ private ForStatementSyntax ParseForStatement(SyntaxList att { this.EatToken(); isDeclaration = ScanType() != ScanTypeFlags.NotType && this.CurrentToken.Kind == SyntaxKind.IdentifierToken; - this.Reset(ref resetPoint); + resetPoint.Reset(); } - - haveScopedKeyword = isDeclaration; } else if (this.CurrentToken.Kind == SyntaxKind.RefKeyword) { @@ -9163,88 +9218,56 @@ private ForStatementSyntax ParseForStatement(SyntaxList att isDeclaration = !this.IsQueryExpression(mayBeVariableDeclaration: true, mayBeMemberDeclaration: false) && this.ScanType() != ScanTypeFlags.NotType && this.IsTrueIdentifier(); - - this.Reset(ref resetPoint); + resetPoint.Reset(); } if (isDeclaration) { - SyntaxToken scopedKeyword = null; - - if (haveScopedKeyword) - { - scopedKeyword = EatContextualToken(SyntaxKind.ScopedKeyword); - } - - decl = ParseParenthesizedVariableDeclaration(); - - var declType = decl.Type; - - if (scopedKeyword != null) - { - declType = _syntaxFactory.ScopedType(scopedKeyword, declType); - } - - if (declType != decl.Type) - { - decl = decl.Update(declType, decl.Variables); - } + return (ParseParenthesizedVariableDeclaration(VariableFlags.ForStatement, ParsePossibleScopedKeyword(isFunctionPointerParameter: false)), initializers: default); } else if (this.CurrentToken.Kind != SyntaxKind.SemicolonToken) { - // Not a type followed by an identifier, so it must be an expression list. - initializers = this.ParseForStatementExpressionList(ref openParen); + // Not a type followed by an identifier, so it must be the initializer expression list. + // + // Do not consume semicolons here as they are used to separate the initializers out from the + // condition of the for loop. + return (variableDeclaration: null, parseForStatementExpressionList(ref openParen, allowSemicolonAsSeparator: false)); } - - var semi = this.EatToken(SyntaxKind.SemicolonToken); - ExpressionSyntax condition = null; - if (this.CurrentToken.Kind != SyntaxKind.SemicolonToken) + else { - condition = this.ParseExpressionCore(); + return default; } + } - var semi2 = this.EatToken(SyntaxKind.SemicolonToken); - if (this.CurrentToken.Kind != SyntaxKind.CloseParenToken) - { - incrementors = this.ParseForStatementExpressionList(ref semi2); - } + SyntaxToken eatCommaOrSemicolon() + => this.CurrentToken.Kind is SyntaxKind.CommaToken + ? this.EatTokenAsKind(SyntaxKind.SemicolonToken) + : this.EatToken(SyntaxKind.SemicolonToken); - return _syntaxFactory.ForStatement( - attributes, - forToken, - openParen, - decl, - initializers, - semi, - condition, - semi2, - incrementors, - this.EatToken(SyntaxKind.CloseParenToken), - ParseEmbeddedStatement()); - } - finally + SyntaxToken eatUnexpectedTokensAndCloseParenToken() { - _termState = saveTerm; - this.Release(ref resetPoint); - } - } + var skippedTokens = _pool.Allocate(); - private bool IsEndOfForStatementArgument() - { - return this.CurrentToken.Kind is SyntaxKind.SemicolonToken or SyntaxKind.CloseParenToken or SyntaxKind.OpenBraceToken; - } + while (this.CurrentToken.Kind is SyntaxKind.SemicolonToken or SyntaxKind.CommaToken) + skippedTokens.Add(this.EatTokenWithPrejudice(SyntaxKind.CloseParenToken)); - private SeparatedSyntaxList ParseForStatementExpressionList(ref SyntaxToken startToken) - { - return ParseCommaSeparatedSyntaxList( - ref startToken, - SyntaxKind.CloseParenToken, - static @this => @this.IsPossibleExpression(), - static @this => @this.ParseExpressionCore(), - skipBadForStatementExpressionListTokens, - allowTrailingSeparator: false, - requireOneElement: false, - allowSemicolonAsSeparator: false); + var result = this.EatToken(SyntaxKind.CloseParenToken); + return AddLeadingSkippedSyntax(result, _pool.ToTokenListAndFree(skippedTokens).Node); + } + + // Parses out a sequence of expressions. Both for the initializer section (the `for (initializer1, + // initializer2, ...` section), as well as the incrementor section (the `for (;; incrementor1, incrementor2, + // ...` section). + SeparatedSyntaxList parseForStatementExpressionList(ref SyntaxToken startToken, bool allowSemicolonAsSeparator) + => ParseCommaSeparatedSyntaxList( + ref startToken, + SyntaxKind.CloseParenToken, + static @this => @this.IsPossibleExpression(), + static @this => @this.ParseExpressionCore(), + skipBadForStatementExpressionListTokens, + allowTrailingSeparator: false, + requireOneElement: false, + allowSemicolonAsSeparator); static PostSkipAction skipBadForStatementExpressionListTokens( LanguageParser @this, ref SyntaxToken startToken, SeparatedSyntaxListBuilder list, SyntaxKind expectedKind, SyntaxKind closeKind) @@ -9259,6 +9282,11 @@ static PostSkipAction skipBadForStatementExpressionListTokens( } } + private bool IsEndOfForStatementArgument() + { + return this.CurrentToken.Kind is SyntaxKind.SemicolonToken or SyntaxKind.CloseParenToken or SyntaxKind.OpenBraceToken; + } + private CommonForEachStatementSyntax ParseForEachStatement( SyntaxList attributes, SyntaxToken awaitTokenOpt) { @@ -9870,8 +9898,7 @@ private void ParseUsingExpression(ref VariableDeclarationSyntax declaration, ref if (scopedKeyword != null) { - declaration = ParseParenthesizedVariableDeclaration(); - declaration = declaration.Update(_syntaxFactory.ScopedType(scopedKeyword, declaration.Type), declaration.Variables); + declaration = ParseParenthesizedVariableDeclaration(VariableFlags.None, scopedKeyword); return; } else @@ -9904,14 +9931,14 @@ private void ParseUsingExpression(ref VariableDeclarationSyntax declaration, ref case SyntaxKind.CommaToken: case SyntaxKind.CloseParenToken: this.Reset(ref resetPoint); - declaration = ParseParenthesizedVariableDeclaration(); + declaration = ParseParenthesizedVariableDeclaration(VariableFlags.None, scopedKeyword: null); break; case SyntaxKind.EqualsToken: // Parse it as a decl. If the next token is a : and only one variable was parsed, // convert the whole thing to ?: expression. this.Reset(ref resetPoint); - declaration = ParseParenthesizedVariableDeclaration(); + declaration = ParseParenthesizedVariableDeclaration(VariableFlags.None, scopedKeyword: null); // We may have non-nullable types in error scenarios. if (this.CurrentToken.Kind == SyntaxKind.ColonToken && @@ -9932,7 +9959,7 @@ private void ParseUsingExpression(ref VariableDeclarationSyntax declaration, ref else if (IsUsingStatementVariableDeclaration(st)) { this.Reset(ref resetPoint); - declaration = ParseParenthesizedVariableDeclaration(); + declaration = ParseParenthesizedVariableDeclaration(VariableFlags.None, scopedKeyword: null); } else { @@ -10023,6 +10050,8 @@ private StatementSyntax ParseLocalDeclarationStatement(SyntaxList /// Parse a local variable declaration for constructs where the variable declaration is enclosed in parentheses. /// Specifically, only for the `fixed (...)` `for(...)` or `using (...)` statements. /// - private VariableDeclarationSyntax ParseParenthesizedVariableDeclaration() + private VariableDeclarationSyntax ParseParenthesizedVariableDeclaration( + VariableFlags initialFlags, SyntaxToken? scopedKeyword) { var variables = _pool.AllocateSeparated(); ParseLocalDeclaration( @@ -10199,6 +10231,8 @@ private VariableDeclarationSyntax ParseParenthesizedVariableDeclaration() stopOnCloseParen: true, attributes: default, mods: default, + scopedKeyword, + initialFlags, out var type, out var localFunction); Debug.Assert(localFunction == null); @@ -10213,12 +10247,17 @@ private void ParseLocalDeclaration( bool stopOnCloseParen, SyntaxList attributes, SyntaxList mods, + SyntaxToken? scopedKeyword, + VariableFlags initialFlags, out TypeSyntax type, out LocalFunctionStatementSyntax localFunction) { type = allowLocalFunctions ? ParseReturnType() : this.ParseType(); - VariableFlags flags = VariableFlags.LocalOrField; + if (scopedKeyword != null) + type = _syntaxFactory.ScopedType(scopedKeyword, type); + + VariableFlags flags = initialFlags | VariableFlags.LocalOrField; if (mods.Any((int)SyntaxKind.ConstKeyword)) { flags |= VariableFlags.Const; @@ -10244,6 +10283,8 @@ private void ParseLocalDeclaration( } } +#nullable disable + private bool IsEndOfDeclarationClause() { switch (this.CurrentToken.Kind) diff --git a/src/Compilers/CSharp/Portable/Parser/Lexer.cs b/src/Compilers/CSharp/Portable/Parser/Lexer.cs index a34f9cdd9b3a3..03b167ef6460f 100644 --- a/src/Compilers/CSharp/Portable/Parser/Lexer.cs +++ b/src/Compilers/CSharp/Portable/Parser/Lexer.cs @@ -303,7 +303,7 @@ private static int GetFullWidth(SyntaxListBuilder? builder) private SyntaxToken LexSyntaxToken() { _leadingTriviaCache.Clear(); - this.LexSyntaxTrivia(afterFirstToken: TextWindow.Position > 0, isTrailing: false, triviaList: ref _leadingTriviaCache); + this.LexSyntaxTrivia(isFollowingToken: TextWindow.Position > 0, isTrailing: false, triviaList: ref _leadingTriviaCache); var leading = _leadingTriviaCache; var tokenInfo = default(TokenInfo); @@ -313,7 +313,7 @@ private SyntaxToken LexSyntaxToken() var errors = this.GetErrors(GetFullWidth(leading)); _trailingTriviaCache.Clear(); - this.LexSyntaxTrivia(afterFirstToken: true, isTrailing: true, triviaList: ref _trailingTriviaCache); + this.LexSyntaxTrivia(isFollowingToken: true, isTrailing: true, triviaList: ref _trailingTriviaCache); var trailing = _trailingTriviaCache; return Create(in tokenInfo, leading, trailing, errors); @@ -322,7 +322,7 @@ private SyntaxToken LexSyntaxToken() internal SyntaxTriviaList LexSyntaxLeadingTrivia() { _leadingTriviaCache.Clear(); - this.LexSyntaxTrivia(afterFirstToken: TextWindow.Position > 0, isTrailing: false, triviaList: ref _leadingTriviaCache); + this.LexSyntaxTrivia(isFollowingToken: TextWindow.Position > 0, isTrailing: false, triviaList: ref _leadingTriviaCache); return new SyntaxTriviaList(default(Microsoft.CodeAnalysis.SyntaxToken), _leadingTriviaCache.ToListNode(), position: 0, index: 0); } @@ -330,7 +330,7 @@ internal SyntaxTriviaList LexSyntaxLeadingTrivia() internal SyntaxTriviaList LexSyntaxTrailingTrivia() { _trailingTriviaCache.Clear(); - this.LexSyntaxTrivia(afterFirstToken: true, isTrailing: true, triviaList: ref _trailingTriviaCache); + this.LexSyntaxTrivia(isFollowingToken: true, isTrailing: true, triviaList: ref _trailingTriviaCache); return new SyntaxTriviaList(default(Microsoft.CodeAnalysis.SyntaxToken), _trailingTriviaCache.ToListNode(), position: 0, index: 0); } @@ -1889,7 +1889,7 @@ private bool ScanIdentifierOrKeyword(ref TokenInfo info) } } - private void LexSyntaxTrivia(bool afterFirstToken, bool isTrailing, ref SyntaxListBuilder triviaList) + private void LexSyntaxTrivia(bool isFollowingToken, bool isTrailing, ref SyntaxListBuilder triviaList) { bool onlyWhitespaceOnLine = !isTrailing; @@ -1995,7 +1995,33 @@ private void LexSyntaxTrivia(bool afterFirstToken, bool isTrailing, ref SyntaxLi case '#': if (_allowPreprocessorDirectives) { - this.LexDirectiveAndExcludedTrivia(afterFirstToken, isTrailing || !onlyWhitespaceOnLine, ref triviaList); + if (isTrailing || !onlyWhitespaceOnLine) + { + // Directives cannot be in trailing trivia. + // We parse the directive (ignoring its effects like disabled text or defined symbols). + // We extract the text corresponding to the parsed directive + // and add it as a BadToken in SkippedTokensTrivia. + + var savePosition = TextWindow.Position; + + // All the `false` arguments affect only error reporting and the resulting node, both of which we discard. + // However, passing `false` can skip some unnecessary checks. + this.ParseDirective(isActive: false, endIsActive: false, isFollowingToken: false); + + var text = TextWindow.Text.GetSubText(TextSpan.FromBounds(savePosition, TextWindow.Position)); + + var error = new SyntaxDiagnosticInfo(offset: 0, width: 1, code: ErrorCode.ERR_BadDirectivePlacement); + + var token = SyntaxFactory.BadToken(null, text.ToString(), null).WithDiagnosticsGreen([error]); + + this.AddTrivia(SyntaxFactory.SkippedTokensTrivia(token), ref triviaList); + } + else + { + this.LexDirectiveAndExcludedTrivia(isFollowingToken, ref triviaList); + } + + onlyWhitespaceOnLine = true; break; } else @@ -2332,13 +2358,12 @@ private static SyntaxTrivia CreateWhitespaceTrivia(SlidingTextWindow textWindow) } private void LexDirectiveAndExcludedTrivia( - bool afterFirstToken, - bool afterNonWhitespaceOnLine, + bool isFollowingToken, ref SyntaxListBuilder triviaList) { - var directive = this.LexSingleDirective(true, true, afterFirstToken, afterNonWhitespaceOnLine, ref triviaList); + var directive = this.LexSingleDirective(true, true, isFollowingToken, ref triviaList); - // also lex excluded stuff + // also lex excluded stuff var branching = directive as BranchingDirectiveTriviaSyntax; if (branching != null && !branching.BranchTaken) { @@ -2362,7 +2387,7 @@ private void LexExcludedDirectivesAndTrivia(bool endIsActive, ref SyntaxListBuil break; } - var directive = this.LexSingleDirective(false, endIsActive, false, false, ref triviaList); + var directive = this.LexSingleDirective(false, endIsActive, false, ref triviaList); var branching = directive as BranchingDirectiveTriviaSyntax; if (directive.Kind == SyntaxKind.EndIfDirectiveTrivia || (branching != null && branching.BranchTaken)) { @@ -2378,8 +2403,7 @@ private void LexExcludedDirectivesAndTrivia(bool endIsActive, ref SyntaxListBuil private CSharpSyntaxNode LexSingleDirective( bool isActive, bool endIsActive, - bool afterFirstToken, - bool afterNonWhitespaceOnLine, + bool isFollowingToken, ref SyntaxListBuilder triviaList) { if (SyntaxFacts.IsWhitespace(TextWindow.PeekChar())) @@ -2388,16 +2412,25 @@ private CSharpSyntaxNode LexSingleDirective( this.AddTrivia(this.ScanWhitespace(), ref triviaList); } - CSharpSyntaxNode directive; + CSharpSyntaxNode directive = ParseDirective(isActive, endIsActive, isFollowingToken); + + this.AddTrivia(directive, ref triviaList); + _directives = directive.ApplyDirectives(_directives); + return directive; + } + + private CSharpSyntaxNode ParseDirective( + bool isActive, + bool endIsActive, + bool isFollowingToken) + { var saveMode = _mode; _directiveParser ??= new DirectiveParser(this); _directiveParser.ReInitialize(_directives); - directive = _directiveParser.ParseDirective(isActive, endIsActive, afterFirstToken, afterNonWhitespaceOnLine); + CSharpSyntaxNode directive = _directiveParser.ParseDirective(isActive, endIsActive, isFollowingToken); - this.AddTrivia(directive, ref triviaList); - _directives = directive.ApplyDirectives(_directives); _mode = saveMode; return directive; } diff --git a/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs b/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs index 71b2a9c52bc36..4c9e5502030c4 100644 --- a/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs @@ -890,13 +890,20 @@ protected static SyntaxDiagnosticInfo MakeError(ErrorCode code, params object[] return new SyntaxDiagnosticInfo(code, args); } - protected TNode AddLeadingSkippedSyntax(TNode node, GreenNode skippedSyntax) where TNode : CSharpSyntaxNode +#nullable enable + + protected TNode AddLeadingSkippedSyntax(TNode node, GreenNode? skippedSyntax) where TNode : CSharpSyntaxNode { + if (skippedSyntax is null) + return node; + var oldToken = node as SyntaxToken ?? node.GetFirstToken(); var newToken = AddSkippedSyntax(oldToken, skippedSyntax, trailing: false); return SyntaxFirstTokenReplacer.Replace(node, oldToken, newToken, skippedSyntax.FullWidth); } +#nullable disable + protected void AddTrailingSkippedSyntax(SyntaxListBuilder list, GreenNode skippedSyntax) { list[list.Count - 1] = AddTrailingSkippedSyntax((CSharpSyntaxNode)list[list.Count - 1], skippedSyntax); diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs index 0825bdf3f429f..0fa99aec99b2f 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs @@ -59,7 +59,15 @@ public override void VisitField(IFieldSymbol symbol) AddPunctuation(SyntaxKind.DotToken); } - if (symbol.ContainingType.TypeKind == TypeKind.Enum) + if (!Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) + && symbol is Symbols.PublicModel.FieldSymbol + && symbol.AssociatedSymbol is IPropertySymbol associatedProperty) + { + AddPropertyNameAndParameters(associatedProperty); + AddPunctuation(SyntaxKind.DotToken); + AddKeyword(SyntaxKind.FieldKeyword); + } + else if (symbol.ContainingType.TypeKind == TypeKind.Enum) { Builder.Add(CreatePart(SymbolDisplayPartKind.EnumMemberName, symbol, symbol.Name)); } @@ -321,7 +329,7 @@ public override void VisitMethod(IMethodSymbol symbol) // If we're using the metadata format, then include the return type. // Otherwise we eschew it since it is redundant in a conversion // signature. - if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames)) + if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames)) { goto default; } @@ -332,7 +340,7 @@ public override void VisitMethod(IMethodSymbol symbol) // If we're using the metadata format, then include the return type. // Otherwise we eschew it since it is redundant in a conversion // signature. - if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) || + if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) || tryGetUserDefinedOperatorTokenKind(symbol.MetadataName) == SyntaxKind.None) { goto default; @@ -462,7 +470,7 @@ public override void VisitMethod(IMethodSymbol symbol) // Note: we are using the metadata name also in the case that // symbol.containingType is null (which should never be the case here) or is an // anonymous type (which 'does not have a name'). - var name = Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) || symbol.ContainingType == null || symbol.ContainingType.IsAnonymousType + var name = Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) || symbol.ContainingType == null || symbol.ContainingType.IsAnonymousType ? symbol.Name : symbol.ContainingType.Name; @@ -476,7 +484,7 @@ public override void VisitMethod(IMethodSymbol symbol) var partKind = GetPartKindForConstructorOrDestructor(symbol); // Note: we are using the metadata name also in the case that symbol.containingType is null, which should never be the case here. - if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) || symbol.ContainingType == null) + if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) || symbol.ContainingType == null) { Builder.Add(CreatePart(partKind, symbol, symbol.Name)); } @@ -491,7 +499,7 @@ public override void VisitMethod(IMethodSymbol symbol) { AddExplicitInterfaceIfNeeded(symbol.ExplicitInterfaceImplementations); - if (!Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) && + if (!Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) && symbol.GetSymbol()?.OriginalDefinition is SourceUserDefinedOperatorSymbolBase sourceUserDefinedOperatorSymbolBase) { var operatorName = symbol.MetadataName; @@ -520,7 +528,7 @@ public override void VisitMethod(IMethodSymbol symbol) case MethodKind.UserDefinedOperator: case MethodKind.BuiltinOperator: { - if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames)) + if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames)) { Builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, symbol.MetadataName)); } @@ -541,7 +549,7 @@ public override void VisitMethod(IMethodSymbol symbol) } case MethodKind.Conversion: { - if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames)) + if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames)) { Builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, symbol.MetadataName)); } diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs index de1d50677f572..a642883cfe1ea 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs @@ -341,6 +341,13 @@ public override void VisitAlias(IAliasSymbol symbol) } } + internal override void VisitPreprocessing(IPreprocessingSymbol symbol) + { + // Currently using 'Text' part kind as there is no kind specific to preprocessing symbols. + var part = new SymbolDisplayPart(SymbolDisplayPartKind.Text, symbol, symbol.Name); + Builder.Add(part); + } + protected override void AddSpace() { Builder.Add(CreatePart(SymbolDisplayPartKind.Space, null, " ")); diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.PropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.PropertySymbol.cs index 14b838693648f..0caa26f8424dc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.PropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.PropertySymbol.cs @@ -201,7 +201,7 @@ public FieldSymbol BackingField get { return _backingField; } } - internal override int? TryGetOverloadResolutionPriority() => null; + internal override int TryGetOverloadResolutionPriority() => 0; public override bool Equals(Symbol obj, TypeCompareKind compareKind) { diff --git a/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs b/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs index 8583f3cb714b9..ca24025fe4390 100644 --- a/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs +++ b/src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Symbols; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols @@ -65,30 +66,50 @@ internal static bool StructDependsOn(NamedTypeSymbol depends, NamedTypeSymbol on Debug.Assert((object)on != null); Debug.Assert(on.IsDefinition); - var hs = PooledHashSet.GetInstance(); - StructDependsClosure(depends, hs, on); + var hs = PooledHashSet.GetInstance(); + var typesWithCycle = PooledHashSet.GetInstance(); + StructDependsClosure(depends, hs, typesWithCycle, ConsList.Empty.Prepend(on)); - var result = hs.Contains(on); + var result = typesWithCycle.Contains(on); + typesWithCycle.Free(); hs.Free(); return result; } - private static void StructDependsClosure(NamedTypeSymbol type, HashSet partialClosure, NamedTypeSymbol on) + private static void StructDependsClosure(NamedTypeSymbol type, HashSet partialClosure, HashSet typesWithCycle, ConsList on) { Debug.Assert((object)type != null); - if ((object)type.OriginalDefinition == on) + if (typesWithCycle.Contains(type.OriginalDefinition)) + { + return; + } + + if (on.ContainsReference(type.OriginalDefinition)) { // found a possibly expanding cycle, for example // struct X { public T t; } // struct W { X>> x; } // while not explicitly forbidden by the spec, it should be. - partialClosure.Add(on); + typesWithCycle.Add(type.OriginalDefinition); return; } if (partialClosure.Add(type)) + { + if (!type.IsDefinition) + { + // First, visit type as a definition in order to detect the fact that it itself has a cycle. + // This prevents us from going into an infinite generic expansion while visiting constructed form + // of the type below. + visitFields(type.OriginalDefinition, partialClosure, typesWithCycle, on.Prepend(type.OriginalDefinition)); + } + + visitFields(type, partialClosure, typesWithCycle, on); + } + + static void visitFields(NamedTypeSymbol type, HashSet partialClosure, HashSet typesWithCycle, ConsList on) { foreach (var member in type.GetMembersUnordered()) { @@ -99,7 +120,7 @@ private static void StructDependsClosure(NamedTypeSymbol type, HashSet p continue; } - StructDependsClosure((NamedTypeSymbol)fieldType, partialClosure, on); + StructDependsClosure((NamedTypeSymbol)fieldType, partialClosure, typesWithCycle, on); } } } @@ -122,7 +143,9 @@ private static void StructDependsClosure(NamedTypeSymbol type, HashSet p /// internal static ManagedKind GetManagedKind(NamedTypeSymbol type, ref CompoundUseSiteInfo useSiteInfo) { - var (isManaged, hasGenerics) = IsManagedTypeHelper(type); + // The code below should be kept in sync with NamedTypeSymbol.GetManagedKind in VB + + var (isManaged, hasGenerics) = INamedTypeSymbolInternal.Helpers.IsManagedTypeHelper(type); var definitelyManaged = isManaged == ThreeState.True; if (isManaged == ThreeState.Unknown) { @@ -201,7 +224,7 @@ internal static ManagedKind GetManagedKind(NamedTypeSymbol type, ref CompoundUse } else { - var result = IsManagedTypeHelper(fieldNamedType); + var result = INamedTypeSymbolInternal.Helpers.IsManagedTypeHelper(fieldNamedType); hasGenerics = hasGenerics || result.hasGenerics; // NOTE: don't use ManagedKind.get on a NamedTypeSymbol - that could lead // to infinite recursion. @@ -235,60 +258,5 @@ internal static ManagedKind GetManagedKind(NamedTypeSymbol type, ref CompoundUse internal static TypeSymbol NonPointerType(this FieldSymbol field) => field.HasPointerType ? null : field.Type; - - /// - /// Returns True or False if we can determine whether the type is managed - /// without looking at its fields and Unknown otherwise. - /// Also returns whether or not the given type is generic. - /// - private static (ThreeState isManaged, bool hasGenerics) IsManagedTypeHelper(NamedTypeSymbol type) - { - // To match dev10, we treat enums as their underlying types. - if (type.IsEnumType()) - { - type = type.GetEnumUnderlyingType(); - } - - // Short-circuit common cases. - switch (type.SpecialType) - { - case SpecialType.System_Void: - case SpecialType.System_Boolean: - case SpecialType.System_Char: - case SpecialType.System_SByte: - case SpecialType.System_Byte: - case SpecialType.System_Int16: - case SpecialType.System_UInt16: - case SpecialType.System_Int32: - case SpecialType.System_UInt32: - case SpecialType.System_Int64: - case SpecialType.System_UInt64: - case SpecialType.System_Decimal: - case SpecialType.System_Single: - case SpecialType.System_Double: - case SpecialType.System_IntPtr: - case SpecialType.System_UIntPtr: - case SpecialType.System_ArgIterator: - case SpecialType.System_RuntimeArgumentHandle: - return (ThreeState.False, false); - case SpecialType.System_TypedReference: - return (ThreeState.True, false); - case SpecialType.None: - default: - // CONSIDER: could provide cases for other common special types. - break; // Proceed with additional checks. - } - - bool hasGenerics = type.IsGenericType; - switch (type.TypeKind) - { - case TypeKind.Enum: - return (ThreeState.False, hasGenerics); - case TypeKind.Struct: - return (ThreeState.Unknown, hasGenerics); - default: - return (ThreeState.True, hasGenerics); - } - } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstructedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ConstructedMethodSymbol.cs index 42085cff169cc..efba1df8176c2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstructedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstructedMethodSymbol.cs @@ -16,7 +16,7 @@ internal sealed class ConstructedMethodSymbol : SubstitutedMethodSymbol private readonly ImmutableArray _typeArgumentsWithAnnotations; internal ConstructedMethodSymbol(MethodSymbol constructedFrom, ImmutableArray typeArgumentsWithAnnotations) - : base(containingSymbol: constructedFrom.ContainingType, + : base(containingSymbol: constructedFrom.ContainingSymbol, map: new TypeMap(constructedFrom.ContainingType, ((MethodSymbol)constructedFrom.OriginalDefinition).TypeParameters, typeArgumentsWithAnnotations), originalDefinition: (MethodSymbol)constructedFrom.OriginalDefinition, constructedFrom: constructedFrom) diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs index a18307dfcc0cc..ac17af6693ab3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs @@ -295,9 +295,9 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil return false; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorPropertySymbol.cs index 136f99adf5013..3662cefe70bbe 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorPropertySymbol.cs @@ -93,6 +93,6 @@ public ErrorPropertySymbol(Symbol containingSymbol, TypeSymbol type, string name public override ImmutableArray RefCustomModifiers { get { return ImmutableArray.Empty; } } - internal override int? TryGetOverloadResolutionPriority() => null; + internal override int TryGetOverloadResolutionPriority() => 0; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs index 760b4fe8674e9..f0ed88d0c3f4c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs @@ -866,9 +866,9 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol? bui return false; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs index c530ef5ba6398..9629eda149760 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs @@ -1702,11 +1702,12 @@ internal override bool HasAsyncMethodBuilderAttribute(out TypeSymbol builderArgu return builderArgument is not null; } - internal override int? TryGetOverloadResolutionPriority() + internal override int TryGetOverloadResolutionPriority() { if (!_packedFlags.IsOverloadResolutionPriorityPopulated) { - if (_containingType.ContainingPEModule.Module.TryGetOverloadResolutionPriorityValue(_handle, out int priority)) + if (_containingType.ContainingPEModule.Module.TryGetOverloadResolutionPriorityValue(_handle, out int priority) && + priority != 0) { Interlocked.CompareExchange(ref AccessUncommonFields()._lazyOverloadResolutionPriority, priority, 0); } @@ -1721,7 +1722,7 @@ internal override bool HasAsyncMethodBuilderAttribute(out TypeSymbol builderArgu _packedFlags.SetIsOverloadResolutionPriorityPopulated(); } - return _uncommonFields?._lazyOverloadResolutionPriority; + return _uncommonFields?._lazyOverloadResolutionPriority ?? 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs index a5f273713f236..0956462733b10 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs @@ -1043,12 +1043,13 @@ internal override bool HasRuntimeSpecialName get { return null; } } - internal override int? TryGetOverloadResolutionPriority() + internal override int TryGetOverloadResolutionPriority() { Debug.Assert(IsIndexer || IsIndexedProperty); if (!_flags.IsOverloadResolutionPriorityPopulated) { - if (_containingType.ContainingPEModule.Module.TryGetOverloadResolutionPriorityValue(_handle, out int priority)) + if (_containingType.ContainingPEModule.Module.TryGetOverloadResolutionPriorityValue(_handle, out int priority) && + priority != 0) { Interlocked.CompareExchange(ref AccessUncommonFields()._lazyOverloadResolutionPriority, priority, 0); } @@ -1063,7 +1064,7 @@ internal override bool HasRuntimeSpecialName _flags.SetOverloadResolutionPriorityPopulated(); } - return _uncommonFields?._lazyOverloadResolutionPriority; + return _uncommonFields?._lazyOverloadResolutionPriority ?? 0; } private sealed class PEPropertySymbolWithCustomModifiers : PEPropertySymbol diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs index a584624035ce3..ae6accbadf890 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.cs @@ -230,7 +230,7 @@ private ImmutableArray GetDeclaredConstraintTypes(ConsList< } // - presence of unmanaged pattern has to be matched with `valuetype` - // - IsUnmanagedAttribute is allowed iff there is an unmanaged pattern + // - IsUnmanagedAttribute is allowed if there is an unmanaged pattern if (hasUnmanagedModreqPattern && (_flags & GenericParameterAttributes.NotNullableValueTypeConstraint) == 0 || hasUnmanagedModreqPattern != peModule.HasIsUnmanagedAttribute(_handle)) { diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs index e1fcf1786b6cc..ac078acaa6101 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs @@ -1192,9 +1192,9 @@ internal virtual void AddSynthesizedReturnTypeAttributes(PEModuleBuilder moduleB /// /// Do not call this method from early attribute binding, cycles will occur. /// - internal int OverloadResolutionPriority => CanHaveOverloadResolutionPriority ? (TryGetOverloadResolutionPriority() ?? 0) : 0; + internal int OverloadResolutionPriority => CanHaveOverloadResolutionPriority ? TryGetOverloadResolutionPriority() : 0; - internal abstract int? TryGetOverloadResolutionPriority(); + internal abstract int TryGetOverloadResolutionPriority(); internal bool CanHaveOverloadResolutionPriority => MethodKind is MethodKind.Ordinary diff --git a/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs index 329beb975e807..40946df3c5950 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PropertySymbol.cs @@ -350,11 +350,11 @@ internal int OverloadResolutionPriority return 0; } - return TryGetOverloadResolutionPriority() ?? 0; + return TryGetOverloadResolutionPriority(); } } - internal abstract int? TryGetOverloadResolutionPriority(); + internal abstract int TryGetOverloadResolutionPriority(); internal bool CanHaveOverloadResolutionPriority => !IsOverride && !IsExplicitInterfaceImplementation && (IsIndexer || IsIndexedProperty); diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs index 065b926fd69e9..f27a5aa53ecc5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/PreprocessingSymbol.cs @@ -65,11 +65,11 @@ bool ISymbol.Equals(ISymbol? other, CodeAnalysis.SymbolEqualityComparer equality Accessibility ISymbol.DeclaredAccessibility => Accessibility.NotApplicable; - void ISymbol.Accept(SymbolVisitor visitor) => throw new System.NotSupportedException(); + void ISymbol.Accept(SymbolVisitor visitor) => visitor.VisitPreprocessing(this); - TResult ISymbol.Accept(SymbolVisitor visitor) => throw new System.NotSupportedException(); + TResult ISymbol.Accept(SymbolVisitor visitor) => visitor.VisitPreprocessing(this)!; - TResult ISymbol.Accept(SymbolVisitor visitor, TArgument argument) => throw new System.NotSupportedException(); + TResult ISymbol.Accept(SymbolVisitor visitor, TArgument argument) => visitor.VisitPreprocessing(this, argument); string? ISymbol.GetDocumentationCommentId() => null; diff --git a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs index 55f514fae9519..5bdf91a8c8508 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs @@ -608,7 +608,7 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil #nullable enable - internal override int? TryGetOverloadResolutionPriority() + internal override int TryGetOverloadResolutionPriority() { return _reducedFrom.TryGetOverloadResolutionPriority(); } diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs index c71b5cbcb5bc8..7afa024b16661 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs @@ -184,7 +184,7 @@ internal override bool IsMetadataFinal internal sealed override bool UseUpdatedEscapeRules => true; - internal sealed override int? TryGetOverloadResolutionPriority() => throw ExceptionUtilities.Unreachable(); + internal sealed override int TryGetOverloadResolutionPriority() => throw ExceptionUtilities.Unreachable(); #endregion } diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs index d2e426f22923f..1ec96ab09bb70 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyPropertySymbol.cs @@ -103,7 +103,7 @@ public SignatureOnlyPropertySymbol( public override bool IsIndexer { get { throw ExceptionUtilities.Unreachable(); } } - internal override int? TryGetOverloadResolutionPriority() => throw ExceptionUtilities.Unreachable(); + internal override int TryGetOverloadResolutionPriority() => throw ExceptionUtilities.Unreachable(); #endregion Not used by PropertySignatureComparer } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs index 90af0833df5fe..4bdec7c9aec71 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs @@ -201,9 +201,9 @@ internal sealed override System.AttributeTargets GetAttributeTarget() return System.AttributeTargets.Delegate; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } private sealed class Constructor : SourceDelegateMethodSymbol diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs index bebafb570db77..5e6846f3883b4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs @@ -177,9 +177,9 @@ internal override bool GenerateDebugInfo get { return true; } } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs index bde10d904f2eb..7a3aa3b89b479 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs @@ -229,9 +229,9 @@ protected string GetOverriddenAccessorName(SourceEventSymbol @event, bool isAdde return null; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 8e64bfd4ed0ae..f94f39639f149 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -2380,13 +2380,23 @@ private bool HasStructCircularity(BindingDiagnosticBag diagnostics) { foreach (var member in valuesByName) { - if (member.Kind != SymbolKind.Field) + FieldSymbol? field; + + // Only instance fields (including field-like events) affect the outcome. + switch (member.Kind) { - // NOTE: don't have to check field-like events, because they can't have struct types. - continue; + case SymbolKind.Field: + field = (FieldSymbol)member; + Debug.Assert(field.AssociatedSymbol is not EventSymbol, "Didn't expect to find a field-like event backing field in the member list."); + break; + case SymbolKind.Event: + field = ((EventSymbol)member).AssociatedField; + break; + default: + continue; } - var field = (FieldSymbol)member; - if (field.IsStatic) + + if (field is null || field.IsStatic) { continue; } @@ -2670,7 +2680,22 @@ private void CheckFiniteFlatteningGraph(BindingDiagnosticBag diagnostics) instanceMap.Add(this, this); foreach (var m in this.GetMembersUnordered()) { - var f = m as FieldSymbol; + FieldSymbol? f; + + // Only instance fields (including field-like events) affect the outcome. + switch (m.Kind) + { + case SymbolKind.Field: + f = (FieldSymbol)m; + Debug.Assert(f.AssociatedSymbol is not EventSymbol, "Didn't expect to find a field-like event backing field in the member list."); + break; + case SymbolKind.Event: + f = ((EventSymbol)m).AssociatedField; + break; + default: + continue; + } + if (f is null || !f.IsStatic || f.Type.TypeKind != TypeKind.Struct) continue; var type = (NamedTypeSymbol)f.Type; if (InfiniteFlatteningGraph(this, type, instanceMap)) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index bb5183e3e9d75..691df206c1abb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -1744,7 +1744,7 @@ internal override System.Reflection.MethodImplAttributes ImplementationAttribute } } - internal override int? TryGetOverloadResolutionPriority() - => GetEarlyDecodedWellKnownAttributeData()?.OverloadResolutionPriority; + internal override int TryGetOverloadResolutionPriority() + => GetEarlyDecodedWellKnownAttributeData()?.OverloadResolutionPriority ?? 0; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs index 2b611cea14e60..197f65d0fcc57 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs @@ -1701,10 +1701,10 @@ private void ValidateIndexerNameAttribute(CSharpAttributeData attribute, Attribu } } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { Debug.Assert(this.IsIndexer); - return GetEarlyDecodedWellKnownAttributeData()?.OverloadResolutionPriority; + return GetEarlyDecodedWellKnownAttributeData()?.OverloadResolutionPriority ?? 0; } #endregion diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs index e31108b59eb3e..45b0527c8d466 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols // C.M is a ConstructedMethodSymbol. internal class SubstitutedMethodSymbol : WrappedMethodSymbol { - private readonly NamedTypeSymbol _containingType; + private readonly Symbol _containingSymbol; private readonly MethodSymbol _underlyingMethod; private readonly TypeMap _inputMap; private readonly MethodSymbol _constructedFrom; @@ -43,11 +43,11 @@ internal SubstitutedMethodSymbol(NamedTypeSymbol containingSymbol, MethodSymbol Debug.Assert(TypeSymbol.Equals(originalDefinition.ContainingType, containingSymbol.OriginalDefinition, TypeCompareKind.ConsiderEverything2)); } - protected SubstitutedMethodSymbol(NamedTypeSymbol containingSymbol, TypeMap map, MethodSymbol originalDefinition, MethodSymbol constructedFrom) + protected SubstitutedMethodSymbol(Symbol containingSymbol, TypeMap map, MethodSymbol originalDefinition, MethodSymbol constructedFrom) { Debug.Assert((object)originalDefinition != null); Debug.Assert(originalDefinition.IsDefinition); - _containingType = containingSymbol; + _containingSymbol = containingSymbol; _underlyingMethod = originalDefinition; _inputMap = map; if ((object)constructedFrom != null) @@ -190,7 +190,7 @@ public sealed override Symbol ContainingSymbol { get { - return _containingType; + return _containingSymbol; } } @@ -198,7 +198,7 @@ public override NamedTypeSymbol ContainingType { get { - return _containingType; + return _containingSymbol is NamedTypeSymbol nt ? nt : _containingSymbol.ContainingType; } } @@ -366,7 +366,7 @@ private int ComputeHashCode() // it's possible that we will compare equal to the original definition under certain conditions // (e.g, ignoring nullability) and want to retain the same hashcode. As such, consider only // the original definition for the hashcode when we know equality is possible - containingHashCode = _containingType.GetHashCode(); + containingHashCode = _containingSymbol.GetHashCode(); if (containingHashCode == this.OriginalDefinition.ContainingType.GetHashCode() && wasConstructedForAnnotations(this)) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListProperty.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListProperty.cs index afbe2a2c285c5..406c84d246d21 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListProperty.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListProperty.cs @@ -78,6 +78,6 @@ internal SynthesizedReadOnlyListProperty( internal override ObsoleteAttributeData? ObsoleteAttributeData => null; - internal override int? TryGetOverloadResolutionPriority() => null; + internal override int TryGetOverloadResolutionPriority() => 0; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordObjectMethod.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordObjectMethod.cs index b3bb0c75a8269..f5872cbed3094 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordObjectMethod.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordObjectMethod.cs @@ -57,9 +57,9 @@ internal static bool VerifyOverridesMethodFromObject(MethodSymbol overriding, Sp return reportAnError; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs index dc3baa3e834d8..62f5a6a4969a3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs @@ -320,9 +320,9 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil return false; } - internal override int? TryGetOverloadResolutionPriority() + internal override int TryGetOverloadResolutionPriority() { - return null; + return 0; } /// A synthesized entrypoint that forwards all calls to an async Main Method diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs index 79de6ffeedf69..811658bfbe4f5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs @@ -362,9 +362,9 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil return false; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs index f8587cdaa0746..a3f190bfafa9d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs @@ -89,9 +89,9 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil return false; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs index a596adb68da1f..5ab93f4506691 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs @@ -514,9 +514,9 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil return false; } - internal override int? TryGetOverloadResolutionPriority() + internal override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs index bcd7cbafad6e0..971dea49f7b2d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs @@ -292,9 +292,9 @@ private static bool IsNullableAnalysisEnabled(CSharpCompilation compilation, Com return false; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs index e7e49a20ee75c..df7e6416e5419 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs @@ -441,9 +441,9 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol? bui return false; } - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { - return null; + return 0; } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs index 0dcfcb6163033..2087167575af1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs @@ -365,7 +365,7 @@ internal override bool GenerateDebugInfo internal sealed override bool UseUpdatedEscapeRules => UnderlyingMethod.UseUpdatedEscapeRules; - internal sealed override int? TryGetOverloadResolutionPriority() + internal sealed override int TryGetOverloadResolutionPriority() { return UnderlyingMethod.TryGetOverloadResolutionPriority(); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedPropertySymbol.cs index 6f8c67a69891f..9cbab5ca03d21 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedPropertySymbol.cs @@ -189,6 +189,6 @@ internal override bool HasRuntimeSpecialName } } - internal override int? TryGetOverloadResolutionPriority() => _underlyingProperty.OverloadResolutionPriority; + internal override int TryGetOverloadResolutionPriority() => _underlyingProperty.OverloadResolutionPriority; } } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 3063434c31f32..a5d4e7d459849 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -2577,6 +2577,11 @@ Inicializátory polí struktury + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift nepodepsaný pravý posun @@ -10305,8 +10310,8 @@ Poskytněte kompilátoru nějaký způsob, jak metody rozlišit. Můžete např - Cannot open '{0}' for writing -- '{1}' - {0} se nedá otevřít pro zápis -- {1}. + Cannot open '{0}' for writing -- {1} + {0} se nedá otevřít pro zápis -- {1}. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 90f477f32f713..49661004a2962 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -2577,6 +2577,11 @@ Strukturfeldinitialisierer + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift Unsignierte Rechtsverschiebung @@ -10305,8 +10310,8 @@ Unterstützen Sie den Compiler bei der Unterscheidung zwischen den Methoden. Daz - Cannot open '{0}' for writing -- '{1}' - "{0}" kann nicht zum Schreiben geöffnet werden: "{1}" + Cannot open '{0}' for writing -- {1} + "{0}" kann nicht zum Schreiben geöffnet werden: "{1}" diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index b02f65a303a4b..581451baade5e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -2577,6 +2577,11 @@ inicializadores de campo de estructura + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift cambio derecho sin firmar @@ -10305,8 +10310,8 @@ Indique al compilador alguna forma de diferenciar los métodos. Por ejemplo, pue - Cannot open '{0}' for writing -- '{1}' - No se puede abrir '{0}' para escribir: '{1}' + Cannot open '{0}' for writing -- {1} + No se puede abrir '{0}' para escribir: '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index e68743f716884..246bddbdd5cc6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -2577,6 +2577,11 @@ initialiseurs de champ de struct + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift shift droit non signé @@ -10305,8 +10310,8 @@ Permettez au compilateur de différencier les méthodes. Par exemple, vous pouve - Cannot open '{0}' for writing -- '{1}' - Impossible d'ouvrir '{0}' en écriture -- '{1}' + Cannot open '{0}' for writing -- {1} + Impossible d'ouvrir '{0}' en écriture -- '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index d740d7581631a..438db326e9229 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -2577,6 +2577,11 @@ inizializzatori di campo struct + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift spostamento a destra senza segno @@ -10305,8 +10310,8 @@ Impostare il compilatore in modo tale da distinguere i metodi, ad esempio assegn - Cannot open '{0}' for writing -- '{1}' - Non è possibile aprire '{0}' per la scrittura - '{1}' + Cannot open '{0}' for writing -- {1} + Non è possibile aprire '{0}' per la scrittura - '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 99aac9bb501f4..e6eb82d333c76 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -2577,6 +2577,11 @@ 構造体フィールド初期化子 + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift 符号なし右シフト @@ -10305,8 +10310,8 @@ C# では out と ref を区別しますが、CLR では同じと認識します - Cannot open '{0}' for writing -- '{1}' - ファイル '{0}' を開いて書き込むことができません -- '{1}' + Cannot open '{0}' for writing -- {1} + ファイル '{0}' を開いて書き込むことができません -- '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index d892a425f8a3a..303ca7e9dc7a5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -2577,6 +2577,11 @@ 구조체 필드 이니셜라이저 + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift 부호 없는 오른쪽 시프트 @@ -10305,8 +10310,8 @@ C#에서는 out과 ref를 구분하지만 CLR에서는 동일한 것으로 간 - Cannot open '{0}' for writing -- '{1}' - '{0}'을(를) 쓰기용으로 열 수 없습니다. '{1}' + Cannot open '{0}' for writing -- {1} + '{0}'을(를) 쓰기용으로 열 수 없습니다. '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index eb751460f7219..a0d2f13e94d99 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -2577,6 +2577,11 @@ inicjatory pola struktury + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift niepodpisane przesunięcie w prawo @@ -10305,8 +10310,8 @@ Musisz umożliwić kompilatorowi rozróżnienie metod. Możesz na przykład nada - Cannot open '{0}' for writing -- '{1}' - Nie można otworzyć „{0}” do zapisu — „{1}” + Cannot open '{0}' for writing -- {1} + Nie można otworzyć „{0}” do zapisu — „{1}” diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 1d7ee3712fe2c..e6828357934e3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -2577,6 +2577,11 @@ inicializadores de campo de struct + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift deslocamento direito não atribuído @@ -10305,8 +10310,8 @@ Forneça ao compilador alguma forma de diferenciar os métodos. Por exemplo, voc - Cannot open '{0}' for writing -- '{1}' - Não é possível abrir "{0}" para escrever -- "{1}" + Cannot open '{0}' for writing -- {1} + Não é possível abrir "{0}" para escrever -- "{1}" diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index bced0025a1c6a..af40b0f4b46af 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -2577,6 +2577,11 @@ инициализаторы полей структуры + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift сдвиг вправо без знака @@ -10306,8 +10311,8 @@ Give the compiler some way to differentiate the methods. For example, you can gi - Cannot open '{0}' for writing -- '{1}' - Не удается открыть "{0}" для записи — "{1}". + Cannot open '{0}' for writing -- {1} + Не удается открыть "{0}" для записи — "{1}". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 4ed19a83eb011..62efb622ebeaf 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -2577,6 +2577,11 @@ struct alan başlatıcıları + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift işaretsiz sağ kaydırma @@ -10305,8 +10310,8 @@ Derleyiciye yöntemleri ayrıştırma yolu verin. Örneğin, bunlara farklı adl - Cannot open '{0}' for writing -- '{1}' - '{0}' yazma için açılamıyor -- '{1}' + Cannot open '{0}' for writing -- {1} + '{0}' yazma için açılamıyor -- '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 1fc9dc681199e..6f8caf4fd86c1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -2577,6 +2577,11 @@ 结构字段初始化表达式 + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift 未签名的右移位 @@ -10305,8 +10310,8 @@ Give the compiler some way to differentiate the methods. For example, you can gi - Cannot open '{0}' for writing -- '{1}' - 无法打开“{0}”进行写入 --“{1}” + Cannot open '{0}' for writing -- {1} + 无法打开“{0}”进行写入 --“{1}” diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index d3fee078125d8..c8ffa8fac8617 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -2577,6 +2577,11 @@ 結構欄位初始設定式 + + unbound generic types in nameof operator + unbound generic types in nameof operator + + unsigned right shift 未簽署右移位 @@ -10305,8 +10310,8 @@ Give the compiler some way to differentiate the methods. For example, you can gi - Cannot open '{0}' for writing -- '{1}' - 無法開啟 '{0}' 進行寫入 -- '{1}' + Cannot open '{0}' for writing -- {1} + 無法開啟 '{0}' 進行寫入 -- '{1}' diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 6147817c4add5..99048d694be53 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -38,6 +38,7 @@ using Basic.Reference.Assemblies; using static Microsoft.CodeAnalysis.CommonDiagnosticAnalyzers; using static Roslyn.Test.Utilities.SharedResourceHelpers; +using System.Diagnostics; namespace Microsoft.CodeAnalysis.CSharp.CommandLine.UnitTests { @@ -50,7 +51,7 @@ public class CommandLineTests : CommandLineTestBase private static readonly string s_CSharpCompilerExecutable = Path.Combine( Path.GetDirectoryName(typeof(CommandLineTests).GetTypeInfo().Assembly.Location), Path.Combine("dependency", "csc.exe")); - private static readonly string s_DotnetCscRun = ExecutionConditionUtil.IsMono ? "mono" : string.Empty; + private static readonly string s_DotnetCscRun = ExecutionConditionUtil.IsMonoDesktop ? "mono" : string.Empty; #endif private static readonly string s_CSharpScriptExecutable; @@ -1727,7 +1728,7 @@ public void LanguageVersionAdded_Canary() // When a new version is added, this test will break. This list must be checked: // - update the "UpgradeProject" codefixer // - update all the tests that call this canary - // - update MaxSupportedLangVersion (a relevant test should break when new version is introduced) + // - update _MaxAvailableLangVersion (a relevant test should break when new version is introduced) // - email release management to add to the release notes (see old example: https://github.com/dotnet/core/pull/1454) AssertEx.SetEqual(new[] { "default", "1", "2", "3", "4", "5", "6", "7.0", "7.1", "7.2", "7.3", "8.0", "9.0", "10.0", "11.0", "12.0", "13.0", "latest", "latestmajor", "preview" }, Enum.GetValues(typeof(LanguageVersion)).Cast().Select(v => v.ToDisplayString())); @@ -4760,7 +4761,7 @@ public void SdkPathAndLibEnvVariable_Relative_csc() } [Fact] - public void UnableWriteOutput() + public void UnableWriteOutput_OutputFileIsDirectory() { var tempFolder = Temp.CreateDirectory(); var baseDirectory = tempFolder.ToString(); @@ -4772,7 +4773,36 @@ public void UnableWriteOutput() var outWriter = new StringWriter(CultureInfo.InvariantCulture); int exitCode = CreateCSharpCompiler(null, baseDirectory, new[] { "/nologo", "/preferreduilang:en", "/t:library", "/out:" + subFolder.ToString(), src.ToString() }).Run(outWriter); Assert.Equal(1, exitCode); - Assert.True(outWriter.ToString().Trim().StartsWith("error CS2012: Cannot open '" + subFolder.ToString() + "' for writing -- '", StringComparison.Ordinal)); // Cannot create a file when that file already exists. + var output = outWriter.ToString().Trim(); + Assert.StartsWith($"error CS2012: Cannot open '{subFolder}' for writing -- ", output); // Cannot create a file when that file already exists. + + CleanupAllGeneratedFiles(src.Path); + } + + [ConditionalFact(typeof(WindowsOnly))] + public void UnableWriteOutput_OutputFileLocked() + { + var tempFolder = Temp.CreateDirectory(); + var baseDirectory = tempFolder.ToString(); + var filePath = tempFolder.CreateFile("temp").Path; + + using var _ = new FileStream(filePath, FileMode.Open, FileAccess.Write, FileShare.None); + var currentProcess = Process.GetCurrentProcess(); + + var src = Temp.CreateFile("a.cs"); + src.WriteAllText("public class C{}"); + + var outWriter = new StringWriter(CultureInfo.InvariantCulture); + int exitCode = CreateCSharpCompiler(responseFile: null, baseDirectory, ["/nologo", "/preferreduilang:en", "/t:library", "/out:" + filePath, src.ToString()]).Run(outWriter); + Assert.Equal(1, exitCode); + var output = outWriter.ToString().Trim(); + + var pattern = @"error CS2012: Cannot open '(?.*)' for writing -- (?.*); file may be locked by '(?.*)' \((?.*)\)"; + var match = Regex.Match(output, pattern); + Assert.True(match.Success, $"Expected pattern:{Environment.NewLine}{pattern}{Environment.NewLine}Actual:{Environment.NewLine}{output}"); + Assert.Equal(filePath, match.Groups["path"].Value); + Assert.Contains("testhost", match.Groups["app"].Value); + Assert.Equal(currentProcess.Id, int.Parse(match.Groups["pid"].Value)); CleanupAllGeneratedFiles(src.Path); } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index a607dbf9aa094..4459d0c3cc867 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -11,6 +11,7 @@ using System.Text; using Basic.Reference.Assemblies; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -181,7 +182,7 @@ public static async Task Main() var v = CompileAndVerify(comp, expectedOutput: "hello world"); v.VerifyIL("C.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { - // Code size 254 (0xfe) + // Code size 268 (0x10c) .maxstack 3 .locals init (int V_0, System.Exception V_1) @@ -198,7 +199,7 @@ .locals init (int V_0, IL_0010: ldarg.0 IL_0011: ldfld ""bool C.d__1.<>w__disposeMode"" IL_0016: brfalse.s IL_001d - IL_0018: leave IL_00d4 + IL_0018: leave IL_00db IL_001d: ldarg.0 IL_001e: ldc.i4.m1 IL_001f: dup @@ -266,11 +267,11 @@ .locals init (int V_0, IL_0096: ldarg.0 IL_0097: ldfld ""bool C.d__1.<>w__disposeMode"" IL_009c: brfalse.s IL_00a0 - IL_009e: leave.s IL_00d4 + IL_009e: leave.s IL_00db IL_00a0: ldarg.0 IL_00a1: ldc.i4.1 IL_00a2: stfld ""bool C.d__1.<>w__disposeMode"" - IL_00a7: leave.s IL_00d4 + IL_00a7: leave.s IL_00db } catch System.Exception { @@ -280,35 +281,41 @@ .locals init (int V_0, IL_00ad: stfld ""int C.d__1.<>1__state"" IL_00b2: ldarg.0 IL_00b3: ldnull - IL_00b4: stfld ""string C.d__1.<>2__current"" + IL_00b4: stfld ""object C.d__1.<>s__1"" IL_00b9: ldarg.0 - IL_00ba: ldflda ""System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__1.<>t__builder"" - IL_00bf: call ""void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()"" - IL_00c4: nop - IL_00c5: ldarg.0 - IL_00c6: ldflda ""System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__1.<>v__promiseOfValueOrEnd"" - IL_00cb: ldloc.1 - IL_00cc: call ""void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)"" - IL_00d1: nop - IL_00d2: leave.s IL_00fd + IL_00ba: ldnull + IL_00bb: stfld ""string C.d__1.<>2__current"" + IL_00c0: ldarg.0 + IL_00c1: ldflda ""System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__1.<>t__builder"" + IL_00c6: call ""void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()"" + IL_00cb: nop + IL_00cc: ldarg.0 + IL_00cd: ldflda ""System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__1.<>v__promiseOfValueOrEnd"" + IL_00d2: ldloc.1 + IL_00d3: call ""void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)"" + IL_00d8: nop + IL_00d9: leave.s IL_010b } - IL_00d4: ldarg.0 - IL_00d5: ldc.i4.s -2 - IL_00d7: stfld ""int C.d__1.<>1__state"" - IL_00dc: ldarg.0 - IL_00dd: ldnull - IL_00de: stfld ""string C.d__1.<>2__current"" + IL_00db: ldarg.0 + IL_00dc: ldc.i4.s -2 + IL_00de: stfld ""int C.d__1.<>1__state"" IL_00e3: ldarg.0 - IL_00e4: ldflda ""System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__1.<>t__builder"" - IL_00e9: call ""void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()"" - IL_00ee: nop - IL_00ef: ldarg.0 - IL_00f0: ldflda ""System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__1.<>v__promiseOfValueOrEnd"" - IL_00f5: ldc.i4.0 - IL_00f6: call ""void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)"" - IL_00fb: nop - IL_00fc: ret - IL_00fd: ret + IL_00e4: ldnull + IL_00e5: stfld ""object C.d__1.<>s__1"" + IL_00ea: ldarg.0 + IL_00eb: ldnull + IL_00ec: stfld ""string C.d__1.<>2__current"" + IL_00f1: ldarg.0 + IL_00f2: ldflda ""System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__1.<>t__builder"" + IL_00f7: call ""void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()"" + IL_00fc: nop + IL_00fd: ldarg.0 + IL_00fe: ldflda ""System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__1.<>v__promiseOfValueOrEnd"" + IL_0103: ldc.i4.0 + IL_0104: call ""void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)"" + IL_0109: nop + IL_010a: ret + IL_010b: ret }"); } @@ -8672,5 +8679,1857 @@ static async System.Threading.Tasks.Task Main() comp2.VerifyEmitDiagnostics(); // Indirectly calling IsMetadataVirtual on S.DisposeAsync (a read which causes the lock to be set) comp1.VerifyEmitDiagnostics(); // Would call EnsureMetadataVirtual on S.DisposeAsync and would therefore assert if S was not already ForceCompleted } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68027")] + public void LambdaWithBindingErrorInYieldReturn() + { + var src = """ +#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +class C +{ + static async IAsyncEnumerable>> BarAsync() + { + yield return async s => + { + await Task.CompletedTask; + throw new NotImplementedException(); + }; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + comp.VerifyDiagnostics(); + + src = """ +#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +class C +{ + static async IAsyncEnumerable>> BarAsync() + { + yield return async s => + { + s // 1 + await Task.CompletedTask; + throw new NotImplementedException(); + }; + } +} +"""; + comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + + comp.VerifyDiagnostics( + // (12,13): error CS0118: 's' is a variable but is used like a type + // s // 1 + Diagnostic(ErrorCode.ERR_BadSKknown, "s").WithArguments("s", "variable", "type").WithLocation(12, 13), + // (13,13): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression + // await Task.CompletedTask; + Diagnostic(ErrorCode.ERR_BadAwaitAsIdentifier, "await").WithLocation(13, 13), + // (13,13): warning CS0168: The variable 'await' is declared but never used + // await Task.CompletedTask; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "await").WithArguments("await").WithLocation(13, 13), + // (13,19): error CS1002: ; expected + // await Task.CompletedTask; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "Task").WithLocation(13, 19), + // (13,19): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // await Task.CompletedTask; + Diagnostic(ErrorCode.ERR_IllegalStatement, "Task.CompletedTask").WithLocation(13, 19)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var s = GetSyntax(tree, "s"); + Assert.Null(model.GetSymbolInfo(s).Symbol); + Assert.Equal(new[] { "System.String s" }, model.GetSymbolInfo(s).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void LambdaWithBindingErrorInReturn() + { + var src = """ +#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously +using System; +using System.Threading.Tasks; + +class C +{ + static async Task>> BarAsync() + { + return async s => + { + await Task.CompletedTask; + throw new NotImplementedException(); + }; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + comp.VerifyDiagnostics(); + + src = """ +#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously +using System; +using System.Threading.Tasks; + +class C +{ + static async Task>> BarAsync() + { + return async s => + { + s // 1 + await Task.CompletedTask; + throw new NotImplementedException(); + }; + } +} +"""; + comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + comp.VerifyDiagnostics( + // (11,13): error CS0118: 's' is a variable but is used like a type + // s // 1 + Diagnostic(ErrorCode.ERR_BadSKknown, "s").WithArguments("s", "variable", "type").WithLocation(11, 13), + // (12,13): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression + // await Task.CompletedTask; + Diagnostic(ErrorCode.ERR_BadAwaitAsIdentifier, "await").WithLocation(12, 13), + // (12,13): warning CS0168: The variable 'await' is declared but never used + // await Task.CompletedTask; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "await").WithArguments("await").WithLocation(12, 13), + // (12,19): error CS1002: ; expected + // await Task.CompletedTask; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "Task").WithLocation(12, 19), + // (12,19): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // await Task.CompletedTask; + Diagnostic(ErrorCode.ERR_IllegalStatement, "Task.CompletedTask").WithLocation(12, 19)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var s = GetSyntax(tree, "s"); + Assert.Null(model.GetSymbolInfo(s).Symbol); + Assert.Equal(new[] { "System.String s" }, model.GetSymbolInfo(s).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74013")] + public void ClearCurrentWhenAwaiting_ReferenceType() + { + var src = """ +using System.Threading.Tasks; +using System.Collections.Generic; + +var tcs = new TaskCompletionSource(); +var enumerable = C.M(tcs.Task); +var enumerator = enumerable.GetAsyncEnumerator(); +if (!await enumerator.MoveNextAsync()) + throw null; + +System.Console.Write(enumerator.Current); + +var promise = enumerator.MoveNextAsync(); +System.Console.Write(enumerator.Current is null); + +tcs.SetResult(); + +if (!await promise) + throw null; + +System.Console.Write(enumerator.Current); + +public class C +{ + public static async IAsyncEnumerable M(Task t) + { + object o = "first "; + yield return o; + await t; + yield return " second"; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + var verifier = CompileAndVerify(comp, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "first True second" : null, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """" +{ + // Code size 349 (0x15d) + .maxstack 3 + .locals init (int V_0, + object V_1, //o + System.Runtime.CompilerServices.TaskAwaiter V_2, + C.d__0 V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -5 + IL_000a: sub + IL_000b: switch ( + IL_00ec, + IL_005a, + IL_0028, + IL_0028, + IL_0028, + IL_00b2) + IL_0028: ldarg.0 + IL_0029: ldfld "bool C.d__0.<>w__disposeMode" + IL_002e: brfalse.s IL_0035 + IL_0030: leave IL_0129 + IL_0035: ldarg.0 + IL_0036: ldc.i4.m1 + IL_0037: dup + IL_0038: stloc.0 + IL_0039: stfld "int C.d__0.<>1__state" + IL_003e: ldstr "first " + IL_0043: stloc.1 + IL_0044: ldarg.0 + IL_0045: ldloc.1 + IL_0046: stfld "object C.d__0.<>2__current" + IL_004b: ldarg.0 + IL_004c: ldc.i4.s -4 + IL_004e: dup + IL_004f: stloc.0 + IL_0050: stfld "int C.d__0.<>1__state" + IL_0055: leave IL_0150 + IL_005a: ldarg.0 + IL_005b: ldc.i4.m1 + IL_005c: dup + IL_005d: stloc.0 + IL_005e: stfld "int C.d__0.<>1__state" + IL_0063: ldarg.0 + IL_0064: ldfld "bool C.d__0.<>w__disposeMode" + IL_0069: brfalse.s IL_0070 + IL_006b: leave IL_0129 + IL_0070: ldarg.0 + IL_0071: ldnull + IL_0072: stfld "object C.d__0.<>2__current" + IL_0077: ldarg.0 + IL_0078: ldfld "System.Threading.Tasks.Task C.d__0.t" + IL_007d: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0082: stloc.2 + IL_0083: ldloca.s V_2 + IL_0085: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_008a: brtrue.s IL_00ce + IL_008c: ldarg.0 + IL_008d: ldc.i4.0 + IL_008e: dup + IL_008f: stloc.0 + IL_0090: stfld "int C.d__0.<>1__state" + IL_0095: ldarg.0 + IL_0096: ldloc.2 + IL_0097: stfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_009c: ldarg.0 + IL_009d: stloc.3 + IL_009e: ldarg.0 + IL_009f: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_00a4: ldloca.s V_2 + IL_00a6: ldloca.s V_3 + IL_00a8: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)" + IL_00ad: leave IL_015c + IL_00b2: ldarg.0 + IL_00b3: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00b8: stloc.2 + IL_00b9: ldarg.0 + IL_00ba: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00bf: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_00c5: ldarg.0 + IL_00c6: ldc.i4.m1 + IL_00c7: dup + IL_00c8: stloc.0 + IL_00c9: stfld "int C.d__0.<>1__state" + IL_00ce: ldloca.s V_2 + IL_00d0: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_00d5: ldarg.0 + IL_00d6: ldstr " second" + IL_00db: stfld "object C.d__0.<>2__current" + IL_00e0: ldarg.0 + IL_00e1: ldc.i4.s -5 + IL_00e3: dup + IL_00e4: stloc.0 + IL_00e5: stfld "int C.d__0.<>1__state" + IL_00ea: leave.s IL_0150 + IL_00ec: ldarg.0 + IL_00ed: ldc.i4.m1 + IL_00ee: dup + IL_00ef: stloc.0 + IL_00f0: stfld "int C.d__0.<>1__state" + IL_00f5: ldarg.0 + IL_00f6: ldfld "bool C.d__0.<>w__disposeMode" + IL_00fb: pop + IL_00fc: leave.s IL_0129 + } + catch System.Exception + { + IL_00fe: stloc.s V_4 + IL_0100: ldarg.0 + IL_0101: ldc.i4.s -2 + IL_0103: stfld "int C.d__0.<>1__state" + IL_0108: ldarg.0 + IL_0109: ldnull + IL_010a: stfld "object C.d__0.<>2__current" + IL_010f: ldarg.0 + IL_0110: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_0115: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_011a: ldarg.0 + IL_011b: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_0120: ldloc.s V_4 + IL_0122: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)" + IL_0127: leave.s IL_015c + } + IL_0129: ldarg.0 + IL_012a: ldc.i4.s -2 + IL_012c: stfld "int C.d__0.<>1__state" + IL_0131: ldarg.0 + IL_0132: ldnull + IL_0133: stfld "object C.d__0.<>2__current" + IL_0138: ldarg.0 + IL_0139: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_013e: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0143: ldarg.0 + IL_0144: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_0149: ldc.i4.0 + IL_014a: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_014f: ret + IL_0150: ldarg.0 + IL_0151: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_0156: ldc.i4.1 + IL_0157: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_015c: ret +} +""""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74013")] + public void ClearCurrentWhenAwaiting_ManagedType() + { + var src = """ +using System.Threading.Tasks; +using System.Collections.Generic; + +var tcs = new TaskCompletionSource(); +var enumerable = C.M(tcs.Task); +var enumerator = enumerable.GetAsyncEnumerator(); +if (!await enumerator.MoveNextAsync()) + throw null; + +System.Console.Write(enumerator.Current.field); + +var promise = enumerator.MoveNextAsync(); +System.Console.Write(enumerator.Current.field is null); + +tcs.SetResult(); + +if (!await promise) + throw null; + +System.Console.Write(enumerator.Current.field); + +public struct S +{ + public object field; +} + +public class C +{ + public static async IAsyncEnumerable M(Task t) + { + object o = "first "; + yield return new S { field = o }; + await t; + yield return new S { field = " second" }; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + var verifier = CompileAndVerify(comp, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "first True second" : null, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74013")] + public void ClearCurrentWhenAwaiting_ValueType() + { + var src = """ +using System.Threading.Tasks; +using System.Collections.Generic; + +var tcs = new TaskCompletionSource(); +var enumerable = C.M(tcs.Task); +var enumerator = enumerable.GetAsyncEnumerator(); +if (!await enumerator.MoveNextAsync()) + throw null; + +System.Console.Write(enumerator.Current); + +var promise = enumerator.MoveNextAsync(); +System.Console.Write(enumerator.Current); + +tcs.SetResult(); + +if (!await promise) + throw null; + +System.Console.Write(enumerator.Current); + +public class C +{ + public static async IAsyncEnumerable M(Task t) + { + yield return 42; + await t; + yield return 43; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + var verifier = CompileAndVerify(comp, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "424243" : null, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74013")] + public void ClearCurrentWhenAwaiting_UnmanagedType() + { + var src = """ +using System.Threading.Tasks; +using System.Collections.Generic; + +var tcs = new TaskCompletionSource(); +var enumerable = C.M(tcs.Task); +var enumerator = enumerable.GetAsyncEnumerator(); +if (!await enumerator.MoveNextAsync()) + throw null; + +System.Console.Write(enumerator.Current.field); + +var promise = enumerator.MoveNextAsync(); +System.Console.Write(enumerator.Current.field); + +tcs.SetResult(); + +if (!await promise) + throw null; + +System.Console.Write(enumerator.Current.field); + +public struct S +{ + public int field; +} +public class C +{ + public static async IAsyncEnumerable M(Task t) + { + yield return new S { field = 42 }; + await t; + yield return new S { field = 43 }; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + var verifier = CompileAndVerify(comp, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "424243" : null, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74013")] + public void ClearCurrentWhenAwaiting_GenericType() + { + var src = """ +using System.Threading.Tasks; +using System.Collections.Generic; + +var tcs = new TaskCompletionSource(); +var enumerable = C.M(tcs.Task, "first ", " second"); +var enumerator = enumerable.GetAsyncEnumerator(); +if (!await enumerator.MoveNextAsync()) + throw null; + +System.Console.Write(enumerator.Current); + +var promise = enumerator.MoveNextAsync(); +System.Console.Write(enumerator.Current is null); + +tcs.SetResult(); + +if (!await promise) + throw null; + +System.Console.Write(enumerator.Current); + +public class C +{ + public static async IAsyncEnumerable M(Task t, T t1, T t2) + { + yield return t1; + await t; + yield return t2; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + var verifier = CompileAndVerify(comp, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "first True second" : null, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """" +{ + // Code size 362 (0x16a) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.TaskAwaiter V_1, + C.d__0 V_2, + System.Exception V_3) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -5 + IL_000a: sub + IL_000b: switch ( + IL_00f1, + IL_0059, + IL_0028, + IL_0028, + IL_0028, + IL_00b6) + IL_0028: ldarg.0 + IL_0029: ldfld "bool C.d__0.<>w__disposeMode" + IL_002e: brfalse.s IL_0035 + IL_0030: leave IL_0131 + IL_0035: ldarg.0 + IL_0036: ldc.i4.m1 + IL_0037: dup + IL_0038: stloc.0 + IL_0039: stfld "int C.d__0.<>1__state" + IL_003e: ldarg.0 + IL_003f: ldarg.0 + IL_0040: ldfld "T C.d__0.t1" + IL_0045: stfld "T C.d__0.<>2__current" + IL_004a: ldarg.0 + IL_004b: ldc.i4.s -4 + IL_004d: dup + IL_004e: stloc.0 + IL_004f: stfld "int C.d__0.<>1__state" + IL_0054: leave IL_015d + IL_0059: ldarg.0 + IL_005a: ldc.i4.m1 + IL_005b: dup + IL_005c: stloc.0 + IL_005d: stfld "int C.d__0.<>1__state" + IL_0062: ldarg.0 + IL_0063: ldfld "bool C.d__0.<>w__disposeMode" + IL_0068: brfalse.s IL_006f + IL_006a: leave IL_0131 + IL_006f: ldarg.0 + IL_0070: ldflda "T C.d__0.<>2__current" + IL_0075: initobj "T" + IL_007b: ldarg.0 + IL_007c: ldfld "System.Threading.Tasks.Task C.d__0.t" + IL_0081: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0086: stloc.1 + IL_0087: ldloca.s V_1 + IL_0089: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_008e: brtrue.s IL_00d2 + IL_0090: ldarg.0 + IL_0091: ldc.i4.0 + IL_0092: dup + IL_0093: stloc.0 + IL_0094: stfld "int C.d__0.<>1__state" + IL_0099: ldarg.0 + IL_009a: ldloc.1 + IL_009b: stfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00a0: ldarg.0 + IL_00a1: stloc.2 + IL_00a2: ldarg.0 + IL_00a3: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_00a8: ldloca.s V_1 + IL_00aa: ldloca.s V_2 + IL_00ac: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)" + IL_00b1: leave IL_0169 + IL_00b6: ldarg.0 + IL_00b7: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00bc: stloc.1 + IL_00bd: ldarg.0 + IL_00be: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00c3: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_00c9: ldarg.0 + IL_00ca: ldc.i4.m1 + IL_00cb: dup + IL_00cc: stloc.0 + IL_00cd: stfld "int C.d__0.<>1__state" + IL_00d2: ldloca.s V_1 + IL_00d4: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_00d9: ldarg.0 + IL_00da: ldarg.0 + IL_00db: ldfld "T C.d__0.t2" + IL_00e0: stfld "T C.d__0.<>2__current" + IL_00e5: ldarg.0 + IL_00e6: ldc.i4.s -5 + IL_00e8: dup + IL_00e9: stloc.0 + IL_00ea: stfld "int C.d__0.<>1__state" + IL_00ef: leave.s IL_015d + IL_00f1: ldarg.0 + IL_00f2: ldc.i4.m1 + IL_00f3: dup + IL_00f4: stloc.0 + IL_00f5: stfld "int C.d__0.<>1__state" + IL_00fa: ldarg.0 + IL_00fb: ldfld "bool C.d__0.<>w__disposeMode" + IL_0100: pop + IL_0101: leave.s IL_0131 + } + catch System.Exception + { + IL_0103: stloc.3 + IL_0104: ldarg.0 + IL_0105: ldc.i4.s -2 + IL_0107: stfld "int C.d__0.<>1__state" + IL_010c: ldarg.0 + IL_010d: ldflda "T C.d__0.<>2__current" + IL_0112: initobj "T" + IL_0118: ldarg.0 + IL_0119: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_011e: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0123: ldarg.0 + IL_0124: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_0129: ldloc.3 + IL_012a: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)" + IL_012f: leave.s IL_0169 + } + IL_0131: ldarg.0 + IL_0132: ldc.i4.s -2 + IL_0134: stfld "int C.d__0.<>1__state" + IL_0139: ldarg.0 + IL_013a: ldflda "T C.d__0.<>2__current" + IL_013f: initobj "T" + IL_0145: ldarg.0 + IL_0146: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_014b: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0150: ldarg.0 + IL_0151: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_0156: ldc.i4.0 + IL_0157: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_015c: ret + IL_015d: ldarg.0 + IL_015e: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_0163: ldc.i4.1 + IL_0164: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_0169: ret +} +""""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74013")] + public void ClearCurrentWhenAwaiting_StructOfGenericType() + { + var src = """ +using System.Threading.Tasks; +using System.Collections.Generic; + +var tcs = new TaskCompletionSource(); +var enumerable = C.M(tcs.Task, "first ", " second"); +var enumerator = enumerable.GetAsyncEnumerator(); +if (!await enumerator.MoveNextAsync()) + throw null; + +System.Console.Write(enumerator.Current.field); + +var promise = enumerator.MoveNextAsync(); +System.Console.Write(enumerator.Current.field is null); + +tcs.SetResult(); + +if (!await promise) + throw null; + +System.Console.Write(enumerator.Current.field); + +public struct S +{ + public T field; +} + +public class C +{ + public static async IAsyncEnumerable> M(Task t, T t1, T t2) + { + yield return new S { field = t1 }; + await t; + yield return new S { field = t2 }; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + var verifier = CompileAndVerify(comp, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "first True second" : null, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74013")] + public void ClearCurrentWhenAwaiting_StructOfUnmanagedGenericType() + { + var src = """ +using System.Threading.Tasks; +using System.Collections.Generic; + +var tcs = new TaskCompletionSource(); +var enumerable = C.M(tcs.Task, 42, 43); +var enumerator = enumerable.GetAsyncEnumerator(); +if (!await enumerator.MoveNextAsync()) + throw null; + +System.Console.Write(enumerator.Current.field); + +var promise = enumerator.MoveNextAsync(); +System.Console.Write(enumerator.Current.field is 0); + +tcs.SetResult(); + +if (!await promise) + throw null; + +System.Console.Write(enumerator.Current.field); + +public struct S where T : unmanaged // UnmanagedWithGenerics +{ + public T field; +} + +public class C +{ + public static async IAsyncEnumerable> M(Task t, T t1, T t2) where T : unmanaged + { + yield return new S { field = t1 }; + await t; + yield return new S { field = t2 }; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + var verifier = CompileAndVerify(comp, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "42False43" : null, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_IntLocal() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +await foreach (int value in values) { } +System.Console.Write(((int)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce() + { + int values2 = 42; + await System.Threading.Tasks.Task.CompletedTask; + yield return values2; + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("42"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +await foreach (int value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce() + { + string values2 = "value "; + await System.Threading.Tasks.Task.CompletedTask; + yield return 1; + System.Console.Write(values2); + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: ExpectedOutput("value value True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ +{ + // Code size 320 (0x140) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.TaskAwaiter V_1, + C.d__0 V_2, + System.Exception V_3) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -4 + IL_000a: sub + IL_000b: switch ( + IL_00b5, + IL_0024, + IL_0024, + IL_0024, + IL_007f) + IL_0024: ldarg.0 + IL_0025: ldfld "bool C.d__0.<>w__disposeMode" + IL_002a: brfalse.s IL_0031 + IL_002c: leave IL_0105 + IL_0031: ldarg.0 + IL_0032: ldc.i4.m1 + IL_0033: dup + IL_0034: stloc.0 + IL_0035: stfld "int C.d__0.<>1__state" + IL_003a: ldarg.0 + IL_003b: ldstr "value " + IL_0040: stfld "string C.d__0.5__2" + IL_0045: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_004a: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_004f: stloc.1 + IL_0050: ldloca.s V_1 + IL_0052: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0057: brtrue.s IL_009b + IL_0059: ldarg.0 + IL_005a: ldc.i4.0 + IL_005b: dup + IL_005c: stloc.0 + IL_005d: stfld "int C.d__0.<>1__state" + IL_0062: ldarg.0 + IL_0063: ldloc.1 + IL_0064: stfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_0069: ldarg.0 + IL_006a: stloc.2 + IL_006b: ldarg.0 + IL_006c: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_0071: ldloca.s V_1 + IL_0073: ldloca.s V_2 + IL_0075: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)" + IL_007a: leave IL_013f + IL_007f: ldarg.0 + IL_0080: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_0085: stloc.1 + IL_0086: ldarg.0 + IL_0087: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_008c: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0092: ldarg.0 + IL_0093: ldc.i4.m1 + IL_0094: dup + IL_0095: stloc.0 + IL_0096: stfld "int C.d__0.<>1__state" + IL_009b: ldloca.s V_1 + IL_009d: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_00a2: ldarg.0 + IL_00a3: ldc.i4.1 + IL_00a4: stfld "int C.d__0.<>2__current" + IL_00a9: ldarg.0 + IL_00aa: ldc.i4.s -4 + IL_00ac: dup + IL_00ad: stloc.0 + IL_00ae: stfld "int C.d__0.<>1__state" + IL_00b3: leave.s IL_0133 + IL_00b5: ldarg.0 + IL_00b6: ldc.i4.m1 + IL_00b7: dup + IL_00b8: stloc.0 + IL_00b9: stfld "int C.d__0.<>1__state" + IL_00be: ldarg.0 + IL_00bf: ldfld "bool C.d__0.<>w__disposeMode" + IL_00c4: brfalse.s IL_00c8 + IL_00c6: leave.s IL_0105 + IL_00c8: ldarg.0 + IL_00c9: ldfld "string C.d__0.5__2" + IL_00ce: call "void System.Console.Write(string)" + IL_00d3: leave.s IL_0105 + } + catch System.Exception + { + IL_00d5: stloc.3 + IL_00d6: ldarg.0 + IL_00d7: ldc.i4.s -2 + IL_00d9: stfld "int C.d__0.<>1__state" + IL_00de: ldarg.0 + IL_00df: ldnull + IL_00e0: stfld "string C.d__0.5__2" + IL_00e5: ldarg.0 + IL_00e6: ldc.i4.0 + IL_00e7: stfld "int C.d__0.<>2__current" + IL_00ec: ldarg.0 + IL_00ed: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_00f2: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_00f7: ldarg.0 + IL_00f8: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_00fd: ldloc.3 + IL_00fe: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)" + IL_0103: leave.s IL_013f + } + IL_0105: ldarg.0 + IL_0106: ldc.i4.s -2 + IL_0108: stfld "int C.d__0.<>1__state" + IL_010d: ldarg.0 + IL_010e: ldnull + IL_010f: stfld "string C.d__0.5__2" + IL_0114: ldarg.0 + IL_0115: ldc.i4.0 + IL_0116: stfld "int C.d__0.<>2__current" + IL_011b: ldarg.0 + IL_011c: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_0121: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0126: ldarg.0 + IL_0127: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_012c: ldc.i4.0 + IL_012d: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_0132: ret + IL_0133: ldarg.0 + IL_0134: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_0139: ldc.i4.1 + IL_013a: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_013f: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal_YieldBreak() + { + string src = """ +using System.Reflection; + +var values = C.Produce(true); +await foreach (int value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b) + { + string s = "value "; + await System.Threading.Tasks.Task.CompletedTask; + yield return 42; + System.Console.Write(s); + if (b) yield break; + throw null; + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("value value True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal_ThrownException() + { + string src = """ +using System.Reflection; + +var values = C.Produce(true); +try +{ + await foreach (int value in values) { } +} +catch (System.Exception e) +{ + System.Console.Write(e.Message); +} + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b) + { + string s = "value "; + await System.Threading.Tasks.Task.CompletedTask; + System.Console.Write(s); + if (b) throw new System.Exception("exception "); + yield break; + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("value exception True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal_EarlyIterationExit() + { + string src = """ +using System.Reflection; + +var values = C.Produce(true); +await foreach (var value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + break; +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b) + { + string s = "value "; + await System.Threading.Tasks.Task.CompletedTask; + yield return 42; + _ = s.ToString(); + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("value True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal() + { + string src = """ +using System.Reflection; + +var tcs = new System.Threading.Tasks.TaskCompletionSource(); +var values = C.Produce(true, tcs.Task); +var enumerator = values.GetAsyncEnumerator(); +assert(await enumerator.MoveNextAsync()); +assert(enumerator.Current == 1); +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + +assert(await enumerator.MoveNextAsync()); +assert(enumerator.Current == 2); +_ = enumerator.MoveNextAsync(); + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b, System.Threading.Tasks.Task task) + { + while (b) + { + string values2 = "value "; + yield return 1; + System.Console.Write(values2); + b = false; + } + yield return 2; + await task; + yield return 3; + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("value value True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_YieldBreak() + { + string src = """ +using System.Reflection; + +var values = C.Produce(true); +var enumerator = values.GetAsyncEnumerator(); +assert(await enumerator.MoveNextAsync()); +assert(enumerator.Current == 1); +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +assert(!(await enumerator.MoveNextAsync())); + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b) + { + while (b) + { + string s = "value "; + yield return 1; + System.Console.Write(s); + await System.Threading.Tasks.Task.CompletedTask; + if (b) yield break; + } + throw null; + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("value value True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_ThrownException() + { + string src = """ +using System.Reflection; + +var values = C.Produce(true); +var enumerator = values.GetAsyncEnumerator(); +assert(await enumerator.MoveNextAsync()); +assert(enumerator.Current == 1); +try +{ + assert(!(await enumerator.MoveNextAsync())); +} +catch (System.Exception e) +{ + System.Console.Write(e.Message); +} +await enumerator.DisposeAsync(); + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b) + { + while (b) + { + string s = "value "; + yield return 1; + System.Console.Write(s); + await System.Threading.Tasks.Task.CompletedTask; + if (b) throw new System.Exception("exception "); + } + throw null; + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("value exception True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_EarlyIterationExit() + { + string src = """ +using System.Reflection; + +var values = C.Produce(true); +await foreach (var value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + break; +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b) + { + while (b) + { + string s = "value "; + yield return 1; + System.Console.Write(s); + await System.Threading.Tasks.Task.CompletedTask; + throw null; + } + throw null; + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("value True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedLocalWithStructFromAnotherCompilation() + { + var libSrc = """ +public struct S +{ + public int field; + public override string ToString() => field.ToString(); +} +"""; + var libComp = CreateCompilation(libSrc, targetFramework: TargetFramework.Net80); + string src = """ +using System.Reflection; + +var tcs = new System.Threading.Tasks.TaskCompletionSource(); +var values = C.Produce(true, tcs.Task); +var enumerator = values.GetAsyncEnumerator(); +assert(await enumerator.MoveNextAsync()); +assert(enumerator.Current == 1); +assert(await enumerator.MoveNextAsync()); +assert(enumerator.Current == 2); +_ = enumerator.MoveNextAsync(); + +System.Console.Write(((S)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b, System.Threading.Tasks.Task task) + { + while (b) + { + S values2 = new S { field = 42 }; + yield return 1; + System.Console.Write(values2); + b = false; + } + yield return 2; + await task; + yield return 3; + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: ExpectedOutput("4242"), references: [libComp.EmitToImageReference()], + verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + + verifier.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ +{ + // Code size 437 (0x1b5) + .maxstack 3 + .locals init (int V_0, + S V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + C.d__0 V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -6 + IL_000a: sub + IL_000b: switch ( + IL_0144, + IL_00bd, + IL_0072, + IL_002c, + IL_002c, + IL_002c, + IL_010e) + IL_002c: ldarg.0 + IL_002d: ldfld "bool C.d__0.<>w__disposeMode" + IL_0032: brfalse.s IL_0039 + IL_0034: leave IL_0181 + IL_0039: ldarg.0 + IL_003a: ldc.i4.m1 + IL_003b: dup + IL_003c: stloc.0 + IL_003d: stfld "int C.d__0.<>1__state" + IL_0042: br.s IL_009f + IL_0044: ldarg.0 + IL_0045: ldloca.s V_1 + IL_0047: initobj "S" + IL_004d: ldloca.s V_1 + IL_004f: ldc.i4.s 42 + IL_0051: stfld "int S.field" + IL_0056: ldloc.1 + IL_0057: stfld "S C.d__0.5__2" + IL_005c: ldarg.0 + IL_005d: ldc.i4.1 + IL_005e: stfld "int C.d__0.<>2__current" + IL_0063: ldarg.0 + IL_0064: ldc.i4.s -4 + IL_0066: dup + IL_0067: stloc.0 + IL_0068: stfld "int C.d__0.<>1__state" + IL_006d: leave IL_01a8 + IL_0072: ldarg.0 + IL_0073: ldc.i4.m1 + IL_0074: dup + IL_0075: stloc.0 + IL_0076: stfld "int C.d__0.<>1__state" + IL_007b: ldarg.0 + IL_007c: ldfld "bool C.d__0.<>w__disposeMode" + IL_0081: brfalse.s IL_0088 + IL_0083: leave IL_0181 + IL_0088: ldarg.0 + IL_0089: ldfld "S C.d__0.5__2" + IL_008e: box "S" + IL_0093: call "void System.Console.Write(object)" + IL_0098: ldarg.0 + IL_0099: ldc.i4.0 + IL_009a: stfld "bool C.d__0.b" + IL_009f: ldarg.0 + IL_00a0: ldfld "bool C.d__0.b" + IL_00a5: brtrue.s IL_0044 + IL_00a7: ldarg.0 + IL_00a8: ldc.i4.2 + IL_00a9: stfld "int C.d__0.<>2__current" + IL_00ae: ldarg.0 + IL_00af: ldc.i4.s -5 + IL_00b1: dup + IL_00b2: stloc.0 + IL_00b3: stfld "int C.d__0.<>1__state" + IL_00b8: leave IL_01a8 + IL_00bd: ldarg.0 + IL_00be: ldc.i4.m1 + IL_00bf: dup + IL_00c0: stloc.0 + IL_00c1: stfld "int C.d__0.<>1__state" + IL_00c6: ldarg.0 + IL_00c7: ldfld "bool C.d__0.<>w__disposeMode" + IL_00cc: brfalse.s IL_00d3 + IL_00ce: leave IL_0181 + IL_00d3: ldarg.0 + IL_00d4: ldfld "System.Threading.Tasks.Task C.d__0.task" + IL_00d9: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_00de: stloc.2 + IL_00df: ldloca.s V_2 + IL_00e1: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_00e6: brtrue.s IL_012a + IL_00e8: ldarg.0 + IL_00e9: ldc.i4.0 + IL_00ea: dup + IL_00eb: stloc.0 + IL_00ec: stfld "int C.d__0.<>1__state" + IL_00f1: ldarg.0 + IL_00f2: ldloc.2 + IL_00f3: stfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00f8: ldarg.0 + IL_00f9: stloc.3 + IL_00fa: ldarg.0 + IL_00fb: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_0100: ldloca.s V_2 + IL_0102: ldloca.s V_3 + IL_0104: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)" + IL_0109: leave IL_01b4 + IL_010e: ldarg.0 + IL_010f: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_0114: stloc.2 + IL_0115: ldarg.0 + IL_0116: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_011b: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0121: ldarg.0 + IL_0122: ldc.i4.m1 + IL_0123: dup + IL_0124: stloc.0 + IL_0125: stfld "int C.d__0.<>1__state" + IL_012a: ldloca.s V_2 + IL_012c: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0131: ldarg.0 + IL_0132: ldc.i4.3 + IL_0133: stfld "int C.d__0.<>2__current" + IL_0138: ldarg.0 + IL_0139: ldc.i4.s -6 + IL_013b: dup + IL_013c: stloc.0 + IL_013d: stfld "int C.d__0.<>1__state" + IL_0142: leave.s IL_01a8 + IL_0144: ldarg.0 + IL_0145: ldc.i4.m1 + IL_0146: dup + IL_0147: stloc.0 + IL_0148: stfld "int C.d__0.<>1__state" + IL_014d: ldarg.0 + IL_014e: ldfld "bool C.d__0.<>w__disposeMode" + IL_0153: pop + IL_0154: leave.s IL_0181 + } + catch System.Exception + { + IL_0156: stloc.s V_4 + IL_0158: ldarg.0 + IL_0159: ldc.i4.s -2 + IL_015b: stfld "int C.d__0.<>1__state" + IL_0160: ldarg.0 + IL_0161: ldc.i4.0 + IL_0162: stfld "int C.d__0.<>2__current" + IL_0167: ldarg.0 + IL_0168: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_016d: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0172: ldarg.0 + IL_0173: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_0178: ldloc.s V_4 + IL_017a: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)" + IL_017f: leave.s IL_01b4 + } + IL_0181: ldarg.0 + IL_0182: ldc.i4.s -2 + IL_0184: stfld "int C.d__0.<>1__state" + IL_0189: ldarg.0 + IL_018a: ldc.i4.0 + IL_018b: stfld "int C.d__0.<>2__current" + IL_0190: ldarg.0 + IL_0191: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_0196: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_019b: ldarg.0 + IL_019c: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_01a1: ldc.i4.0 + IL_01a2: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_01a7: ret + IL_01a8: ldarg.0 + IL_01a9: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_01ae: ldc.i4.1 + IL_01af: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_01b4: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_InTryFinally_WithThrow_EarlyIterationExit() + { + string src = """ +using System.Reflection; + +var values = C.Produce(true); +try +{ + await foreach (var value in values) { break; } // we interrupt the iteration early +} +catch (System.Exception e) +{ + System.Console.Write(e.Message); +} + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(bool b) + { + try + { + string s = "value "; + await System.Threading.Tasks.Task.CompletedTask; + yield return 42; + s.ToString(); + throw null; + } + finally + { + throw new System.Exception("exception "); + } + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("exception True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal_IAsyncEnumerator() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +assert(await values.MoveNextAsync()); +assert(values.Current == 1); +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +assert(!(await values.MoveNextAsync())); +await values.DisposeAsync(); + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +static void assert(bool b) { if (!b) throw new System.Exception(); } + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerator Produce() + { + string s = "value "; + await System.Threading.Tasks.Task.CompletedTask; + yield return 1; + System.Console.Write(s); + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("value value True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NotCleanedTooSoon() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +await foreach (int i in values) +{ + System.Console.Write((values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + break; +} +System.Console.Write((values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce() + { + try + { + string s = "value "; + try + { + await System.Threading.Tasks.Task.CompletedTask; + yield return 42; + } + finally + { + System.Console.Write(s); + } + } + finally + { + System.Console.Write("outer "); + } + } +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("value value outer True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_HoistedFromRefExpression() + { + string src = """ +using System.Reflection; + +var c = new C(); +var values = Program.Produce(c); +await foreach (int i in values) +{ + System.Console.Write((values.GetType().GetField("<>7__wrap3", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + System.Console.Write($" {i} "); +} +System.Console.Write((values.GetType().GetField("<>7__wrap3", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +partial class Program +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(C x) + { + int i = 0; + foreach (var y in x.F) + { + await System.Threading.Tasks.Task.CompletedTask; + yield return i++; + } + } +} + +class C +{ + public Buffer2 F = default; +} + +[System.Runtime.CompilerServices.InlineArray(2)] +public struct Buffer2 +{ + private T _element0; +} +"""; + CompileAndVerify(src, expectedOutput: ExpectedOutput("False 0 False 1 True"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_HoistedFromRefExpression_Debug() + { + string src = """ +using System.Reflection; + +var c = new C(); +var values = Program.Produce(c); +await foreach (int i in values) +{ + System.Console.Write((values.GetType().GetField("<>s__4", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + System.Console.Write($" {i} "); +} +System.Console.Write((values.GetType().GetField("<>s__4", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +partial class Program +{ + public static async System.Collections.Generic.IAsyncEnumerable Produce(C x) + { + int i = 0; + foreach (var y in x.F) + { + await System.Threading.Tasks.Task.CompletedTask; + yield return i++; + } + } +} + +class C +{ + public Buffer2 F = default; +} + +[System.Runtime.CompilerServices.InlineArray(2)] +public struct Buffer2 +{ + private T _element0; +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: ExpectedOutput("False 0 False 1 True"), + verify: Verification.Skipped, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ +{ + // Code size 449 (0x1c1) + .maxstack 4 + .locals init (int V_0, + System.Runtime.CompilerServices.TaskAwaiter V_1, + Program.d__1 V_2, + int V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.d__1.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -4 + IL_000a: sub + IL_000b: switch ( + IL_0026, + IL_002b, + IL_0032, + IL_0032, + IL_002d) + IL_0024: br.s IL_0032 + IL_0026: br IL_0118 + IL_002b: br.s IL_0032 + IL_002d: br IL_00ce + IL_0032: ldarg.0 + IL_0033: ldfld "bool Program.d__1.<>w__disposeMode" + IL_0038: brfalse.s IL_003f + IL_003a: leave IL_0183 + IL_003f: ldarg.0 + IL_0040: ldc.i4.m1 + IL_0041: dup + IL_0042: stloc.0 + IL_0043: stfld "int Program.d__1.<>1__state" + IL_0048: nop + IL_0049: ldarg.0 + IL_004a: ldc.i4.0 + IL_004b: stfld "int Program.d__1.5__1" + IL_0050: nop + IL_0051: ldarg.0 + IL_0052: ldarg.0 + IL_0053: ldfld "C Program.d__1.x" + IL_0058: stfld "C Program.d__1.<>s__4" + IL_005d: ldarg.0 + IL_005e: ldfld "C Program.d__1.<>s__4" + IL_0063: ldfld "Buffer2 C.F" + IL_0068: pop + IL_0069: ldarg.0 + IL_006a: ldc.i4.0 + IL_006b: stfld "int Program.d__1.<>s__2" + IL_0070: br IL_013a + IL_0075: ldarg.0 + IL_0076: ldarg.0 + IL_0077: ldfld "C Program.d__1.<>s__4" + IL_007c: ldflda "Buffer2 C.F" + IL_0081: ldarg.0 + IL_0082: ldfld "int Program.d__1.<>s__2" + IL_0087: call "ref int .InlineArrayElementRef, int>(ref Buffer2, int)" + IL_008c: ldind.i4 + IL_008d: stfld "int Program.d__1.5__3" + IL_0092: nop + IL_0093: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_0098: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_009d: stloc.1 + IL_009e: ldloca.s V_1 + IL_00a0: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_00a5: brtrue.s IL_00ea + IL_00a7: ldarg.0 + IL_00a8: ldc.i4.0 + IL_00a9: dup + IL_00aa: stloc.0 + IL_00ab: stfld "int Program.d__1.<>1__state" + IL_00b0: ldarg.0 + IL_00b1: ldloc.1 + IL_00b2: stfld "System.Runtime.CompilerServices.TaskAwaiter Program.d__1.<>u__1" + IL_00b7: ldarg.0 + IL_00b8: stloc.2 + IL_00b9: ldarg.0 + IL_00ba: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_00bf: ldloca.s V_1 + IL_00c1: ldloca.s V_2 + IL_00c3: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.AwaitUnsafeOnCompletedd__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Program.d__1)" + IL_00c8: nop + IL_00c9: leave IL_01c0 + IL_00ce: ldarg.0 + IL_00cf: ldfld "System.Runtime.CompilerServices.TaskAwaiter Program.d__1.<>u__1" + IL_00d4: stloc.1 + IL_00d5: ldarg.0 + IL_00d6: ldflda "System.Runtime.CompilerServices.TaskAwaiter Program.d__1.<>u__1" + IL_00db: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_00e1: ldarg.0 + IL_00e2: ldc.i4.m1 + IL_00e3: dup + IL_00e4: stloc.0 + IL_00e5: stfld "int Program.d__1.<>1__state" + IL_00ea: ldloca.s V_1 + IL_00ec: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_00f1: nop + IL_00f2: ldarg.0 + IL_00f3: ldarg.0 + IL_00f4: ldfld "int Program.d__1.5__1" + IL_00f9: stloc.3 + IL_00fa: ldarg.0 + IL_00fb: ldloc.3 + IL_00fc: ldc.i4.1 + IL_00fd: add + IL_00fe: stfld "int Program.d__1.5__1" + IL_0103: ldloc.3 + IL_0104: stfld "int Program.d__1.<>2__current" + IL_0109: ldarg.0 + IL_010a: ldc.i4.s -4 + IL_010c: dup + IL_010d: stloc.0 + IL_010e: stfld "int Program.d__1.<>1__state" + IL_0113: leave IL_01b3 + IL_0118: ldarg.0 + IL_0119: ldc.i4.m1 + IL_011a: dup + IL_011b: stloc.0 + IL_011c: stfld "int Program.d__1.<>1__state" + IL_0121: ldarg.0 + IL_0122: ldfld "bool Program.d__1.<>w__disposeMode" + IL_0127: brfalse.s IL_012b + IL_0129: leave.s IL_0183 + IL_012b: nop + IL_012c: ldarg.0 + IL_012d: ldarg.0 + IL_012e: ldfld "int Program.d__1.<>s__2" + IL_0133: ldc.i4.1 + IL_0134: add + IL_0135: stfld "int Program.d__1.<>s__2" + IL_013a: ldarg.0 + IL_013b: ldfld "int Program.d__1.<>s__2" + IL_0140: ldc.i4.2 + IL_0141: blt IL_0075 + IL_0146: ldarg.0 + IL_0147: ldnull + IL_0148: stfld "C Program.d__1.<>s__4" + IL_014d: leave.s IL_0183 + } + catch System.Exception + { + IL_014f: stloc.s V_4 + IL_0151: ldarg.0 + IL_0152: ldc.i4.s -2 + IL_0154: stfld "int Program.d__1.<>1__state" + IL_0159: ldarg.0 + IL_015a: ldnull + IL_015b: stfld "C Program.d__1.<>s__4" + IL_0160: ldarg.0 + IL_0161: ldc.i4.0 + IL_0162: stfld "int Program.d__1.<>2__current" + IL_0167: ldarg.0 + IL_0168: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_016d: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0172: nop + IL_0173: ldarg.0 + IL_0174: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_0179: ldloc.s V_4 + IL_017b: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)" + IL_0180: nop + IL_0181: leave.s IL_01c0 + } + IL_0183: ldarg.0 + IL_0184: ldc.i4.s -2 + IL_0186: stfld "int Program.d__1.<>1__state" + IL_018b: ldarg.0 + IL_018c: ldnull + IL_018d: stfld "C Program.d__1.<>s__4" + IL_0192: ldarg.0 + IL_0193: ldc.i4.0 + IL_0194: stfld "int Program.d__1.<>2__current" + IL_0199: ldarg.0 + IL_019a: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_019f: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_01a4: nop + IL_01a5: ldarg.0 + IL_01a6: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_01ab: ldc.i4.0 + IL_01ac: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_01b1: nop + IL_01b2: ret + IL_01b3: ldarg.0 + IL_01b4: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_01b9: ldc.i4.1 + IL_01ba: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_01bf: nop + IL_01c0: ret +} +"""); + } + + [Fact] + public void AddVariableCleanup_Unmanaged_UseSiteError() + { + var missingLibS1 = CreateCompilation(@" +public struct S1 +{ + public int i; +} +", assemblyName: "libS1", targetFramework: TargetFramework.Net80).ToMetadataReference(); + + var libS2 = CreateCompilation(@" +public struct S2 +{ + public S1 s1; +} +", references: [missingLibS1], assemblyName: "libS2", targetFramework: TargetFramework.Net80).ToMetadataReference(); + + var source = @" +class C +{ + public static async System.Collections.Generic.IAsyncEnumerable M(S2 p) + { + S2 local = p; + await System.Threading.Tasks.Task.CompletedTask; + yield return local; + System.Console.Write(local); + } +} +"; + var comp = CreateCompilation(source, references: [libS2], targetFramework: TargetFramework.Net80); + comp.VerifyEmitDiagnostics( + // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1), + // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1), + // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1), + // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1)); + + comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll, references: [libS2, missingLibS1], targetFramework: TargetFramework.Net80); + comp.VerifyEmitDiagnostics(); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs index 5906b205ab039..89482c2907c11 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; @@ -19,6 +18,11 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { public class CodeGenAsyncTests : EmitMetadataTestBase { + internal static string ExpectedOutput(string output) + { + return ExecutionConditionUtil.IsMonoOrCoreClr ? output : null; + } + private static CSharpCompilation CreateCompilation(string source, IEnumerable references = null, CSharpCompilationOptions options = null) { options = options ?? TestOptions.ReleaseExe; @@ -6053,5 +6057,215 @@ public async Task M(object o) var comp = CSharpTestBase.CreateCompilation(source); comp.VerifyEmitDiagnostics(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal() + { + string src = """ +using System.Reflection; + +var tcs = new System.Threading.Tasks.TaskCompletionSource(); +var task = C.ProduceAsync(true, tcs.Task); + +var callback = (System.Delegate)task.GetType().GetField("m_action", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(task); +object stateMachineBox = callback.Target; +object stateMachine = stateMachineBox.GetType().GetField("StateMachine", BindingFlags.Public | BindingFlags.Instance).GetValue(stateMachineBox); + +System.Console.Write((string)stateMachine.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(stateMachine) is null); + +class C +{ + public static async System.Threading.Tasks.Task ProduceAsync(bool b, System.Threading.Tasks.Task task) + { + while (b) + { + string values2 = "value "; + await System.Threading.Tasks.Task.CompletedTask; + System.Console.Write(values2); + b = false; + } + await task; // block execution here to check what's in the field for "values2" + return 42; + } +} +"""; + // Note: nested hoisted local gets cleared when exiting nested scope normally + CompileAndVerify(src, expectedOutput: ExpectedOutput("value True"), targetFramework: TargetFramework.Net90, verify: Verification.Skipped).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedLocalWithStructFromAnotherCompilation() + { + var libSrc = """ +public struct S +{ + public int field; + public override string ToString() => field.ToString(); +} +"""; + var libComp = CreateCompilation(libSrc, targetFramework: TargetFramework.Net90); + string src = """ +using System.Reflection; + +var tcs = new System.Threading.Tasks.TaskCompletionSource(); +var task = C.ProduceAsync(true, tcs.Task); + +var callback = (System.Delegate)task.GetType().GetField("m_action", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(task); +object stateMachineBox = callback.Target; +object stateMachine = stateMachineBox.GetType().GetField("StateMachine", BindingFlags.Public | BindingFlags.Instance).GetValue(stateMachineBox); + +System.Console.Write((S)stateMachine.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(stateMachine)); + +class C +{ + public static async System.Threading.Tasks.Task ProduceAsync(bool b, System.Threading.Tasks.Task task) + { + while (b) + { + S values2 = new S { field = 42 }; + await System.Threading.Tasks.Task.CompletedTask; + System.Console.Write(values2); + b = false; + } + await task; // block execution here to check what's in the field for "values2" + return 10; + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: ExpectedOutput("4242"), references: [libComp.EmitToImageReference()], + targetFramework: TargetFramework.Net90, verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ +{ + // Code size 309 (0x135) + .maxstack 3 + .locals init (int V_0, + int V_1, + S V_2, + System.Runtime.CompilerServices.TaskAwaiter V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0065 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq IL_00df + IL_0011: br IL_009f + IL_0016: ldarg.0 + IL_0017: ldloca.s V_2 + IL_0019: initobj "S" + IL_001f: ldloca.s V_2 + IL_0021: ldc.i4.s 42 + IL_0023: stfld "int S.field" + IL_0028: ldloc.2 + IL_0029: stfld "S C.d__0.5__2" + IL_002e: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_0033: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0038: stloc.3 + IL_0039: ldloca.s V_3 + IL_003b: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0040: brtrue.s IL_0081 + IL_0042: ldarg.0 + IL_0043: ldc.i4.0 + IL_0044: dup + IL_0045: stloc.0 + IL_0046: stfld "int C.d__0.<>1__state" + IL_004b: ldarg.0 + IL_004c: ldloc.3 + IL_004d: stfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_0052: ldarg.0 + IL_0053: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder" + IL_0058: ldloca.s V_3 + IL_005a: ldarg.0 + IL_005b: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)" + IL_0060: leave IL_0134 + IL_0065: ldarg.0 + IL_0066: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_006b: stloc.3 + IL_006c: ldarg.0 + IL_006d: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_0072: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0078: ldarg.0 + IL_0079: ldc.i4.m1 + IL_007a: dup + IL_007b: stloc.0 + IL_007c: stfld "int C.d__0.<>1__state" + IL_0081: ldloca.s V_3 + IL_0083: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0088: ldarg.0 + IL_0089: ldfld "S C.d__0.5__2" + IL_008e: box "S" + IL_0093: call "void System.Console.Write(object)" + IL_0098: ldarg.0 + IL_0099: ldc.i4.0 + IL_009a: stfld "bool C.d__0.b" + IL_009f: ldarg.0 + IL_00a0: ldfld "bool C.d__0.b" + IL_00a5: brtrue IL_0016 + IL_00aa: ldarg.0 + IL_00ab: ldfld "System.Threading.Tasks.Task C.d__0.task" + IL_00b0: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_00b5: stloc.3 + IL_00b6: ldloca.s V_3 + IL_00b8: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_00bd: brtrue.s IL_00fb + IL_00bf: ldarg.0 + IL_00c0: ldc.i4.1 + IL_00c1: dup + IL_00c2: stloc.0 + IL_00c3: stfld "int C.d__0.<>1__state" + IL_00c8: ldarg.0 + IL_00c9: ldloc.3 + IL_00ca: stfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00cf: ldarg.0 + IL_00d0: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder" + IL_00d5: ldloca.s V_3 + IL_00d7: ldarg.0 + IL_00d8: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)" + IL_00dd: leave.s IL_0134 + IL_00df: ldarg.0 + IL_00e0: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00e5: stloc.3 + IL_00e6: ldarg.0 + IL_00e7: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00ec: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_00f2: ldarg.0 + IL_00f3: ldc.i4.m1 + IL_00f4: dup + IL_00f5: stloc.0 + IL_00f6: stfld "int C.d__0.<>1__state" + IL_00fb: ldloca.s V_3 + IL_00fd: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0102: ldc.i4.s 10 + IL_0104: stloc.1 + IL_0105: leave.s IL_0120 + } + catch System.Exception + { + IL_0107: stloc.s V_4 + IL_0109: ldarg.0 + IL_010a: ldc.i4.s -2 + IL_010c: stfld "int C.d__0.<>1__state" + IL_0111: ldarg.0 + IL_0112: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder" + IL_0117: ldloc.s V_4 + IL_0119: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_011e: leave.s IL_0134 + } + IL_0120: ldarg.0 + IL_0121: ldc.i4.s -2 + IL_0123: stfld "int C.d__0.<>1__state" + IL_0128: ldarg.0 + IL_0129: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder" + IL_012e: ldloc.1 + IL_012f: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult(int)" + IL_0134: ret +} +"""); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDisplayClassOptimisationTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDisplayClassOptimisationTests.cs index 03941adc0d1c2..9de4494e5794c 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDisplayClassOptimisationTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDisplayClassOptimisationTests.cs @@ -7742,15 +7742,24 @@ 01 00 00 00 ) .override method instance void [mscorlib]System.IDisposable::Dispose() // Method begins at RVA 0x20b1 - // Code size 1 (0x1) + // Code size 22 (0x16) .maxstack 8 - IL_0000: ret + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld class C/'<>c__DisplayClass0_0' C/'d__0'::'<>8__1' + IL_0007: ldarg.0 + IL_0008: ldnull + IL_0009: stfld class C/'<>c__DisplayClass0_1' C/'d__0'::'<>8__2' + IL_000e: ldarg.0 + IL_000f: ldnull + IL_0010: stfld class C/'<>c__DisplayClass0_2' C/'d__0'::'<>8__3' + IL_0015: ret } // end of method 'd__0'::System.IDisposable.Dispose .method private final hidebysig newslot virtual instance bool MoveNext () cil managed { .override method instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() - // Method begins at RVA 0x20b4 + // Method begins at RVA 0x20c8 // Code size 342 (0x156) .maxstack 2 .locals init ( @@ -7889,7 +7898,7 @@ .method private final hidebysig specialname newslot virtual 01 00 00 00 ) .override method instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() - // Method begins at RVA 0x2216 + // Method begins at RVA 0x222a // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 @@ -7903,7 +7912,7 @@ instance void System.Collections.IEnumerator.Reset () cil managed 01 00 00 00 ) .override method instance void [mscorlib]System.Collections.IEnumerator::Reset() - // Method begins at RVA 0x221e + // Method begins at RVA 0x2232 // Code size 6 (0x6) .maxstack 8 IL_0000: newobj instance void [mscorlib]System.NotSupportedException::.ctor() @@ -7916,7 +7925,7 @@ instance object System.Collections.IEnumerator.get_Current () cil managed 01 00 00 00 ) .override method instance object [mscorlib]System.Collections.IEnumerator::get_Current() - // Method begins at RVA 0x2225 + // Method begins at RVA 0x2239 // Code size 12 (0xc) .maxstack 8 IL_0000: ldarg.0 @@ -7931,7 +7940,7 @@ .method private final hidebysig newslot virtual 01 00 00 00 ) .override method instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() - // Method begins at RVA 0x2234 + // Method begins at RVA 0x2248 // Code size 43 (0x2b) .maxstack 2 .locals init ( @@ -7964,7 +7973,7 @@ instance class [mscorlib]System.Collections.IEnumerator System.Collections.IEnum 01 00 00 00 ) .override method instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator() - // Method begins at RVA 0x226b + // Method begins at RVA 0x227f // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenExprLambdaTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenExprLambdaTests.cs index 8a54dd3d2e36f..2a479263ed080 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenExprLambdaTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenExprLambdaTests.cs @@ -10,6 +10,8 @@ using Roslyn.Test.Utilities; using Xunit; using Basic.Reference.Assemblies; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Linq; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { @@ -1815,6 +1817,77 @@ static string P(S? s) S"); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72571")] + public void LambdaWithBindingErrorInInitializerOfTargetTypedNew() + { + var src = """ +AddConfig(new() +{ + A = a => + { + a // 1 + }, +}); + +static void AddConfig(Config config) { } + +class Config +{ + public System.Action A { get; set; } +} + +class A { } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,10): error CS1002: ; expected + // a // 1 + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 10)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var s = GetSyntax(tree, "a"); + Assert.Equal("A a", model.GetSymbolInfo(s).Symbol.ToTestDisplayString()); + Assert.Equal(new string[] { }, model.GetSymbolInfo(s).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72571")] + public void LambdaWithBindingErrorInInitializerWithReadonlyTarget() + { + var src = """ +AddConfig(new Config() +{ + A = a => + { + a // 1 + }, +}); + +static void AddConfig(Config config) { } + +class Config +{ + public System.Action A { get; } +} + +class A { } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,5): error CS0200: Property or indexer 'Config.A' cannot be assigned to -- it is read only + // A = a => + Diagnostic(ErrorCode.ERR_AssgReadonlyProp, "A").WithArguments("Config.A").WithLocation(3, 5), + // (5,10): error CS1002: ; expected + // a // 1 + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 10)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var s = GetSyntax(tree, "a"); + Assert.Equal("A a", model.GetSymbolInfo(s).Symbol.ToTestDisplayString()); + Assert.Equal(new string[] { }, model.GetSymbolInfo(s).CandidateSymbols.ToTestDisplayStrings()); + } + #region Regression Tests [WorkItem(544159, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544159")] diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenIterators.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenIterators.cs index 3f29bbdb28868..5ab7b180a4923 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenIterators.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenIterators.cs @@ -3001,5 +3001,1124 @@ .locals init (int V_0, IL_0044: ret }"); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_IntLocal() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +foreach (int value in values) { } +System.Console.Write(((int)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + int values2 = 42; + yield return values2; + System.Console.Write($"{values2} "); + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "42 42").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +foreach (int value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + string values2 = "ran "; + yield return 42; + System.Console.Write(values2); + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "ran ran True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld "string C.d__0.5__2" + IL_0007: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal_YieldBreak() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +foreach (int value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + string s = "ran "; + yield return 42; + System.Console.Write(s); + yield break; + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "ran ran True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld "string C.d__0.5__2" + IL_0007: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_IntArrayLocal() + { + string source = """ +using System.Linq; +using System.Reflection; + +var values = C.Produce(); +foreach (int value in values) +{ + System.Console.Write(((int[])values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)).Length); +} +System.Console.Write(((int[])values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + int[] values2 = Enumerable.Range(0, 100).ToArray(); + yield return 42; + System.Console.Write($" {values2.Length} "); + } +} +"""; + var comp = CreateCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: "100 100 True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld "int[] C.d__0.5__2" + IL_0007: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal() + { + var src = """ +using System.Reflection; + +var enumerable = C.M(true); +var enumerator = enumerable.GetEnumerator(); +try +{ + assert(enumerator.MoveNext()); + System.Console.Write($"{enumerator.Current} "); + System.Console.Write(((string)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator))); + assert(!enumerator.MoveNext()); + System.Console.Write(((string)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator)) is null); +} +finally +{ + enumerator.Dispose(); +} + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +public class C +{ + public static System.Collections.Generic.IEnumerable M(bool b) + { + while (b) + { + string s = "value "; + yield return 42; + b = false; + System.Console.Write(s); + } + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "42 value value True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld "string C.d__0.5__2" + IL_0007: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_YieldBreak() + { + var src = """ +using System.Reflection; + +var enumerable = C.M(true); +var enumerator = enumerable.GetEnumerator(); +try +{ + assert(enumerator.MoveNext()); + System.Console.Write($"{enumerator.Current} "); + assert(!enumerator.MoveNext()); + System.Console.Write(((string)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator))); +} +finally +{ + enumerator.Dispose(); +} +System.Console.Write(((string)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator)) is null); + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +public class C +{ + public static System.Collections.Generic.IEnumerable M(bool b) + { + while (b) + { + string s = "value "; + yield return 42; + System.Console.Write(s); + yield break; + } + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "42 value value True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld "string C.d__0.5__2" + IL_0007: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_ThrownException() + { + var src = """ +using System.Reflection; + +var enumerable = C.M(true); +var enumerator = enumerable.GetEnumerator(); +try +{ + assert(enumerator.MoveNext()); + System.Console.Write($"{enumerator.Current} "); +} +finally +{ + System.Console.Write(((string)enumerable.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerable))); + enumerator.Dispose(); +} +System.Console.Write(((string)enumerable.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerable)) is null); + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +public class C +{ + public static System.Collections.Generic.IEnumerable M(bool b) + { + while (b) + { + string s = "value "; + yield return 42; + System.Console.Write(s); + throw new System.Exception(); + } + } +} +"""; + CompileAndVerify(src, expectedOutput: "42 value True").VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_IntParameter() + { + string src = """ +using System.Reflection; + +var values = C.Produce(42); +foreach (int value in values) { } +System.Console.Write(((int)values.GetType().GetField("s", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +System.Console.Write(((int)values.GetType().GetField("<>3__s", BindingFlags.Public | BindingFlags.Instance).GetValue(values))); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce(int s) + { + yield return 0; + System.Console.Write($"{s} "); + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "42 4242").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringParameter() + { + string src = """ +using System.Reflection; + +var values = C.Produce("value "); +foreach (int value in values) { } +System.Console.Write(((string)values.GetType().GetField("s", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +System.Console.Write(((string)values.GetType().GetField("<>3__s", BindingFlags.Public | BindingFlags.Instance).GetValue(values))); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce(string s) + { + yield return 0; + System.Console.Write(s); + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "value value value").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_ClosureOverLocal() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +foreach (int value in values) { } +var closure = values.GetType().GetField("<>8__1", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values); +System.Console.Write((int)(closure.GetType().GetField("s", BindingFlags.Public | BindingFlags.Instance).GetValue(closure))); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + int s = 41; + local(); + yield return 0; + System.Console.Write($"{s} "); + + void local() + { + s++; + } + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "42 42").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_ClosureOverThis() + { + string src = """ +using System.Reflection; + +class C +{ + int field = 41; + public static void Main() + { + var values = new C().Produce(); + foreach (int value in values) { } + System.Console.Write(((C)values.GetType().GetField("<>4__this", BindingFlags.Public | BindingFlags.Instance).GetValue(values)).field); + } + + private System.Collections.Generic.IEnumerable Produce() + { + local(); + yield return this.field; + + void local() + { + this.field++; + } + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "42").VerifyDiagnostics(); + verifier.VerifyIL("C.d__2.System.IDisposable.Dispose()", """ +{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_InTryFinally() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +foreach (int value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + try + { + var s = "value "; + yield return 0; + System.Console.Write(s); + } + finally + { + System.Console.Write("ran "); + } + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "value value ran True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 34 (0x22) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -3 + IL_000a: beq.s IL_0010 + IL_000c: ldloc.0 + IL_000d: ldc.i4.1 + IL_000e: bne.un.s IL_001a + IL_0010: nop + .try + { + IL_0011: leave.s IL_001a + } + finally + { + IL_0013: ldarg.0 + IL_0014: call "void C.d__0.<>m__Finally1()" + IL_0019: endfinally + } + IL_001a: ldarg.0 + IL_001b: ldnull + IL_001c: stfld "string C.d__0.5__2" + IL_0021: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_InTryFinally_WithThrow() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); + +try +{ + foreach (int value in values) + { + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + } +} +catch (System.Exception e) +{ + System.Console.Write(e.Message); +} + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + try + { + string s = "value "; + yield return 0; + System.Console.Write(s); + } + finally + { + throw new System.Exception("exception "); + } + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "value value exception True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 34 (0x22) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -3 + IL_000a: beq.s IL_0010 + IL_000c: ldloc.0 + IL_000d: ldc.i4.1 + IL_000e: bne.un.s IL_001a + IL_0010: nop + .try + { + IL_0011: leave.s IL_001a + } + finally + { + IL_0013: ldarg.0 + IL_0014: call "void C.d__0.<>m__Finally1()" + IL_0019: endfinally + } + IL_001a: ldarg.0 + IL_001b: ldnull + IL_001c: stfld "string C.d__0.5__2" + IL_0021: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_InTryFinally_WithThrow_EarlyIterationExit() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); + +try +{ + foreach (int value in values) { break; } // we interrupt the iteration early +} +catch (System.Exception e) +{ + System.Console.Write(e.Message); +} + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + try + { + string s = "value"; + yield return 0; + s.ToString(); + throw null; + } + finally + { + throw new System.Exception("exception "); + } + } +} +"""; + // Note: nested hoisted local does not get cleared when an exception is thrown during disposal + CompileAndVerify(src, expectedOutput: "exception value").VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal_WithThrownException() + { + string src = """ +using System.Reflection; + +var values = C.Produce(true); + +try +{ + foreach (int value in values) { } +} +catch (System.Exception e) +{ + System.Console.Write(e.Message); +} + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce(bool b) + { + string s = "value "; + if (b) throw new System.Exception("exception "); + yield return 0; + System.Console.Write(s); + } +} +"""; + CompileAndVerify(src, expectedOutput: "exception True").VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedUnmanagedTypeParameterLocal() + { + var src = """ +using System.Reflection; + +var enumerable = C.M(true, 42); +var enumerator = enumerable.GetEnumerator(); +try +{ + assert(enumerator.MoveNext()); + System.Console.Write($"{enumerator.Current} "); + assert(!enumerator.MoveNext()); + System.Console.Write(" "); + System.Console.Write(((int)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator))); +} +finally +{ + enumerator.Dispose(); +} + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +public class C +{ + public static System.Collections.Generic.IEnumerable M(bool b, T t) where T : unmanaged + { + while (b) + { + T local = t; + yield return 10; + b = false; + System.Console.Write(local); + } + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "10 42 42").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedLocalWithStructFromAnotherCompilation() + { + var libSrc = """ +public struct S +{ + public int field; + public override string ToString() => field.ToString(); +} +"""; + var libComp = CreateCompilation(libSrc); + var src = """ +using System.Reflection; + +var enumerable = C.M(true, new S { field = 42 }); +var enumerator = enumerable.GetEnumerator(); +try +{ + assert(enumerator.MoveNext()); + System.Console.Write($"{enumerator.Current} "); + assert(!enumerator.MoveNext()); + System.Console.Write(" "); + System.Console.Write(((S)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator))); +} +finally +{ + enumerator.Dispose(); +} + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +public class C +{ + public static System.Collections.Generic.IEnumerable M(bool b, S s) + { + while (b) + { + S local = s; + yield return 10; + b = false; + System.Console.Write(local.ToString()); + } + } +} +"""; + + var verifier = CompileAndVerify(src, expectedOutput: "10 42 42", references: [libComp.EmitToImageReference()]).VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedUnmanagedWithGenericsLocal() + { + var src = """ +using System.Reflection; + +var enumerable = C.M(true, 42); +var enumerator = enumerable.GetEnumerator(); +try +{ + assert(enumerator.MoveNext()); + System.Console.Write($"{enumerator.Current} "); + assert(!enumerator.MoveNext()); + System.Console.Write(((S)enumerator.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator)).field); +} +finally +{ + enumerator.Dispose(); +} + +void assert(bool b) +{ + if (!b) throw new System.Exception(); +} + +public struct S where T : unmanaged // UnmanagedWithGenerics +{ + public T field; +} + +public class C +{ + public static System.Collections.Generic.IEnumerable M(bool b, T t) where T : unmanaged + { + while (b) + { + S s = new S { field = t }; + yield return 42; + b = false; + System.Console.Write(s.field); + } + } +} +"""; + CompileAndVerify(src, expectedOutput: "42 4242").VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal_EarlyIterationExit() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); + +foreach (int value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + break; +} + +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + string s = "value "; + yield return 0; + s.ToString(); + throw null; + } +} +"""; + CompileAndVerify(src, expectedOutput: "value True").VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NestedStringLocal_Reused() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +foreach (int value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + { + string values2 = "values2 "; + yield return 42; + System.Console.Write(values2); + } + { + string values3 = "values3 "; + yield return 43; + System.Console.Write(values3); + } + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "values2 values2 values3 values3 True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld "string C.d__0.5__2" + IL_0007: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_Parameters() + { + string src = """ +string s = "ran "; +var values = C.Produce(s); +foreach (int value in values) { } +foreach (int value in values) { } + +class C +{ + public static System.Collections.Generic.IEnumerable Produce(string s) + { + yield return 42; + System.Console.Write(s); + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "ran ran").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_NotCleanedTooSoon() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +foreach (int value in values) +{ + System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); + break; +} +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +class C +{ + public static System.Collections.Generic.IEnumerable Produce() + { + try + { + string s = "value "; + try + { + yield return 42; + } + finally + { + System.Console.Write(s); + } + } + finally + { + System.Console.Write("outer "); + } + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "value value outer True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 55 (0x37) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -4 + IL_000a: sub + IL_000b: ldc.i4.1 + IL_000c: ble.un.s IL_0012 + IL_000e: ldloc.0 + IL_000f: ldc.i4.1 + IL_0010: bne.un.s IL_002f + IL_0012: nop + .try + { + IL_0013: ldloc.0 + IL_0014: ldc.i4.s -4 + IL_0016: beq.s IL_001e + IL_0018: ldloc.0 + IL_0019: ldc.i4.1 + IL_001a: beq.s IL_001e + IL_001c: leave.s IL_002f + IL_001e: nop + .try + { + IL_001f: leave.s IL_002f + } + finally + { + IL_0021: ldarg.0 + IL_0022: call "void C.d__0.<>m__Finally2()" + IL_0027: endfinally + } + } + finally + { + IL_0028: ldarg.0 + IL_0029: call "void C.d__0.<>m__Finally1()" + IL_002e: endfinally + } + IL_002f: ldarg.0 + IL_0030: ldnull + IL_0031: stfld "string C.d__0.5__2" + IL_0036: ret +} +"""); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void AddVariableCleanup_HoistedFromRefExpression() + { + var src = """ +using System.Reflection; + +C c = new C(); +var coll = Test(c); +var enumerator = coll.GetEnumerator(); +try +{ + enumerator.MoveNext(); + System.Console.Write(((C)coll.GetType().GetField("<>7__wrap2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(coll)) is null); +} +finally +{ + enumerator.Dispose(); +} + +System.Console.Write(((C)coll.GetType().GetField("<>7__wrap2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(coll)) is null); + +class C +{ + public Buffer4 F = default; +} + +partial class Program +{ + static System.Collections.Generic.IEnumerable Test(C x) + { + foreach (var y in x.F) + { + yield return -1; + } + } +} + +[System.Runtime.CompilerServices.InlineArray(4)] +public struct Buffer4 +{ + private T _element0; +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + var verifier = CompileAndVerify(comp, expectedOutput: "FalseTrue", verify: Verification.Skipped).VerifyDiagnostics(); + verifier.VerifyIL("Program.d__1.System.IDisposable.Dispose()", """ +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld "C Program.d__1.<>7__wrap2" + IL_0007: ret +} +"""); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] + public void AddVariableCleanup_StringLocal_IEnumerator() + { + string src = """ +using System.Reflection; + +var values = C.Produce(); +assert(values.MoveNext()); +assert(values.Current == 42); +assert(!values.MoveNext()); +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); +values.Dispose(); +System.Console.Write(((string)values.GetType().GetField("5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); + +static void assert(bool b) { if (!b) throw new System.Exception(); } + +class C +{ + public static System.Collections.Generic.IEnumerator Produce() + { + string s = "ran "; + yield return 42; + System.Console.Write(s); + } +} +"""; + var verifier = CompileAndVerify(src, expectedOutput: "ran ran True").VerifyDiagnostics(); + verifier.VerifyIL("C.d__0.System.IDisposable.Dispose()", """ +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: stfld "string C.d__0.5__2" + IL_0007: ret +} +"""); + } + + [Fact] + public void AddVariableCleanup_Unmanaged_UseSiteError() + { + var missingLibS1 = CreateCompilation(@" +public struct S1 +{ + public int i; +} +", assemblyName: "libS1").ToMetadataReference(); + + var libS2 = CreateCompilation(@" +public struct S2 +{ + public S1 s1; +} +", references: [missingLibS1], assemblyName: "libS2").ToMetadataReference(); + + var source = @" +class C +{ + System.Collections.Generic.IEnumerable M1() + { + S2 s2 = M2(); + yield return 42; + System.Console.Write(s2); + } + + S2 M2() => default; +} +"; + var comp = CreateCompilation(source, references: [libS2]); + comp.VerifyEmitDiagnostics( + // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1), + // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1)); + + comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll, references: [libS2, missingLibS1]); + comp.VerifyEmitDiagnostics(); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj b/src/Compilers/CSharp/Test/Emit/Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj index 3276fae69d6c0..a79819fbe934a 100644 --- a/src/Compilers/CSharp/Test/Emit/Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj +++ b/src/Compilers/CSharp/Test/Emit/Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj @@ -27,6 +27,7 @@ + diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs index be32d75c9af80..41169ac72a8ff 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs @@ -5176,7 +5176,7 @@ .maxstack 2 v0.VerifyIL("C.d__0.System.IDisposable.Dispose", @" { - // Code size 33 (0x21) + // Code size 40 (0x28) .maxstack 2 .locals init (int V_0) IL_0000: ldarg.0 @@ -5202,12 +5202,15 @@ .locals init (int V_0) IL_001d: endfinally } IL_001e: br.s IL_0020 - IL_0020: ret + IL_0020: ldarg.0 + IL_0021: ldnull + IL_0022: stfld ""System.IDisposable C.d__0.5__1"" + IL_0027: ret } "); diff1.VerifyIL("C.d__0.System.IDisposable.Dispose", @" { - // Code size 35 (0x23) + // Code size 42 (0x2a) .maxstack 2 .locals init (int V_0) IL_0000: ldarg.0 @@ -5235,7 +5238,10 @@ .locals init (int V_0) IL_001f: endfinally } IL_0020: br.s IL_0022 - IL_0022: ret + IL_0022: ldarg.0 + IL_0023: ldnull + IL_0024: stfld ""System.IDisposable C.d__0.5__1"" + IL_0029: ret } "); @@ -5493,7 +5499,7 @@ static IEnumerable F() diff1.VerifyIL("C.d__0.System.Collections.IEnumerator.MoveNext", @" { - // Code size 526 (0x20e) + // Code size 514 (0x202) .maxstack 2 .locals init (bool V_0, int V_1, @@ -5514,7 +5520,7 @@ .locals init (bool V_0, IL_0022: br IL_0165 IL_0027: ldc.i4.0 IL_0028: stloc.0 - IL_0029: leave IL_020c + IL_0029: leave IL_0200 IL_002e: ldarg.0 IL_002f: ldc.i4.m1 IL_0030: stfld ""int C.d__0.<>1__state"" @@ -5557,7 +5563,7 @@ .locals init (bool V_0, IL_00a6: ldarg.0 IL_00a7: ldc.i4.s -8 IL_00a9: stfld ""int C.d__0.<>1__state"" - IL_00ae: br IL_01a6 + IL_00ae: br IL_019a IL_00b3: ldarg.0 IL_00b4: ldarg.0 IL_00b5: ldfld ""System.Collections.Generic.IEnumerator> C.d__0.<>s__6"" @@ -5608,7 +5614,7 @@ .locals init (bool V_0, IL_013c: stfld ""int C.d__0.<>1__state"" IL_0141: ldc.i4.1 IL_0142: stloc.0 - IL_0143: leave IL_020c + IL_0143: leave IL_0200 IL_0148: ldarg.0 IL_0149: ldc.i4.s -10 IL_014b: stfld ""int C.d__0.<>1__state"" @@ -5620,7 +5626,7 @@ .locals init (bool V_0, IL_0159: stfld ""int C.d__0.<>1__state"" IL_015e: ldc.i4.1 IL_015f: stloc.0 - IL_0160: leave IL_020c + IL_0160: leave IL_0200 IL_0165: ldarg.0 IL_0166: ldc.i4.s -10 IL_0168: stfld ""int C.d__0.<>1__state"" @@ -5642,57 +5648,54 @@ .locals init (bool V_0, IL_0194: ldnull IL_0195: stfld ""System.Collections.Generic.IEnumerator> C.d__0.<>s__8"" IL_019a: ldarg.0 - IL_019b: ldflda ""System.ValueTuple C.d__0.5__7"" - IL_01a0: initobj ""System.ValueTuple"" - IL_01a6: ldarg.0 - IL_01a7: ldfld ""System.Collections.Generic.IEnumerator> C.d__0.<>s__6"" - IL_01ac: callvirt ""bool System.Collections.IEnumerator.MoveNext()"" - IL_01b1: brtrue IL_00b3 - IL_01b6: ldarg.0 - IL_01b7: call ""void C.d__0.<>m__Finally6()"" - IL_01bc: nop - IL_01bd: ldarg.0 - IL_01be: ldnull - IL_01bf: stfld ""System.Collections.Generic.IEnumerator> C.d__0.<>s__6"" - IL_01c4: ldarg.0 - IL_01c5: call ""void C.d__0.<>m__Finally5()"" - IL_01ca: nop - IL_01cb: ldarg.0 - IL_01cc: call ""void C.d__0.<>m__Finally4()"" - IL_01d1: nop - IL_01d2: ldarg.0 - IL_01d3: ldnull - IL_01d4: stfld ""System.IDisposable C.d__0.5__4"" - IL_01d9: ldarg.0 - IL_01da: ldnull - IL_01db: stfld ""System.IDisposable C.d__0.5__5"" - IL_01e0: ldarg.0 - IL_01e1: call ""void C.d__0.<>m__Finally3()"" - IL_01e6: nop - IL_01e7: ldarg.0 - IL_01e8: ldnull - IL_01e9: stfld ""System.IDisposable C.d__0.<>s__3"" - IL_01ee: ldc.i4.0 - IL_01ef: stloc.0 - IL_01f0: br.s IL_01f2 - IL_01f2: ldarg.0 - IL_01f3: call ""void C.d__0.<>m__Finally2()"" - IL_01f8: nop - IL_01f9: br.s IL_01fb - IL_01fb: ldarg.0 - IL_01fc: call ""void C.d__0.<>m__Finally1()"" - IL_0201: nop - IL_0202: leave.s IL_020c + IL_019b: ldfld ""System.Collections.Generic.IEnumerator> C.d__0.<>s__6"" + IL_01a0: callvirt ""bool System.Collections.IEnumerator.MoveNext()"" + IL_01a5: brtrue IL_00b3 + IL_01aa: ldarg.0 + IL_01ab: call ""void C.d__0.<>m__Finally6()"" + IL_01b0: nop + IL_01b1: ldarg.0 + IL_01b2: ldnull + IL_01b3: stfld ""System.Collections.Generic.IEnumerator> C.d__0.<>s__6"" + IL_01b8: ldarg.0 + IL_01b9: call ""void C.d__0.<>m__Finally5()"" + IL_01be: nop + IL_01bf: ldarg.0 + IL_01c0: call ""void C.d__0.<>m__Finally4()"" + IL_01c5: nop + IL_01c6: ldarg.0 + IL_01c7: ldnull + IL_01c8: stfld ""System.IDisposable C.d__0.5__4"" + IL_01cd: ldarg.0 + IL_01ce: ldnull + IL_01cf: stfld ""System.IDisposable C.d__0.5__5"" + IL_01d4: ldarg.0 + IL_01d5: call ""void C.d__0.<>m__Finally3()"" + IL_01da: nop + IL_01db: ldarg.0 + IL_01dc: ldnull + IL_01dd: stfld ""System.IDisposable C.d__0.<>s__3"" + IL_01e2: ldc.i4.0 + IL_01e3: stloc.0 + IL_01e4: br.s IL_01e6 + IL_01e6: ldarg.0 + IL_01e7: call ""void C.d__0.<>m__Finally2()"" + IL_01ec: nop + IL_01ed: br.s IL_01ef + IL_01ef: ldarg.0 + IL_01f0: call ""void C.d__0.<>m__Finally1()"" + IL_01f5: nop + IL_01f6: leave.s IL_0200 } fault { - IL_0204: ldarg.0 - IL_0205: call ""void C.d__0.Dispose()"" - IL_020a: nop - IL_020b: endfinally + IL_01f8: ldarg.0 + IL_01f9: call ""void C.d__0.Dispose()"" + IL_01fe: nop + IL_01ff: endfinally } - IL_020c: ldloc.0 - IL_020d: ret + IL_0200: ldloc.0 + IL_0201: ret }"); } diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/UnmanagedTypeModifierTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/UnmanagedTypeModifierTests.cs index 3436b9a10cb13..dfc1fbea048ae 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/UnmanagedTypeModifierTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/UnmanagedTypeModifierTests.cs @@ -282,6 +282,70 @@ public static void Main() ); } + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/75681")] + public void LoadingUnmanagedTypeModifier_ModreqGeneric() + { + var ilSource = IsUnmanagedAttributeIL + @" +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor +} + +.class public auto ansi beforefieldinit System.Runtime.InteropServices.UnmanagedType`1 + extends [mscorlib]System.Object +{ + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } +} +"; + + var reference = CompileIL(ilSource, prependDefaultHeader: false); + + var code = @" +public class Test +{ + public static void Main() + { + var obj = new TestRef(); + + obj.M1(); + } +}"; + + CreateCompilation(code, references: new[] { reference }).VerifyDiagnostics( + // An error is expected here, see https://github.com/dotnet/roslyn/issues/75681 + ); + } + [Fact] public void LoadingUnmanagedTypeModifier_AttributeWithoutModreq() { diff --git a/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs b/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs index fb7367886e267..d95d92200b540 100644 --- a/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs @@ -10464,6 +10464,74 @@ .locals init (System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V VerifyMergedProperties(actualProperties, actualFields); } + [WorkItem("https://github.com/dotnet/roslyn/issues/75893")] + [Theory] + [CombinatorialData] + public void SpeculativeSemanticModel_01(bool includeLocal) + { + string source = $$""" + class C + { + object P + { + get + { + {{(includeLocal ? "object field = null;" : "")}} + return null; + } + } + } + """; + + var parseOptions = TestOptions.RegularPreview; + var comp = CreateCompilation(source, parseOptions: parseOptions); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var previousAccessor = tree.GetRoot().DescendantNodes().OfType().Single(); + + var modifiedTree = SyntaxFactory.ParseSyntaxTree(source.Replace("return null;", "return field;"), parseOptions); + var modifiedAccessor = modifiedTree.GetRoot().DescendantNodes().OfType().Single(); + Assert.True(model.TryGetSpeculativeSemanticModelForMethodBody(previousAccessor.Body.SpanStart, modifiedAccessor, out var speculativeModel)); + var expr = modifiedAccessor.DescendantNodes().OfType().Single(); + Assert.Equal("return field;", expr.Parent.ToString()); + var symbolInfo = speculativeModel.GetSymbolInfo(expr); + Assert.Null(symbolInfo.Symbol); + } + + [Theory] + [CombinatorialData] + public void SpeculativeSemanticModel_02(bool includeLocal) + { + string source = $$""" + class C + { + object P + { + get + { + {{(includeLocal ? "object field = null;" : "")}} + return null; + } + set; + } + } + """; + + var parseOptions = TestOptions.RegularPreview; + var comp = CreateCompilation(source, parseOptions: parseOptions); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var previousAccessor = tree.GetRoot().DescendantNodes().OfType().First(); + + var modifiedTree = SyntaxFactory.ParseSyntaxTree(source.Replace("return null;", "return field;"), parseOptions); + var modifiedAccessor = modifiedTree.GetRoot().DescendantNodes().OfType().First(); + Assert.True(model.TryGetSpeculativeSemanticModelForMethodBody(previousAccessor.Body.SpanStart, modifiedAccessor, out var speculativeModel)); + var expr = modifiedAccessor.DescendantNodes().OfType().Single(); + Assert.Equal("return field;", expr.Parent.ToString()); + var symbolInfo = speculativeModel.GetSymbolInfo(expr); + Assert.Equal("System.Object C.

k__BackingField", symbolInfo.Symbol.ToTestDisplayString()); + } + [Theory] [InlineData("{ get; }")] [InlineData("{ get; set; }")] diff --git a/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs b/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs index c97ea2eba9aca..0bf1e55bfbc15 100644 --- a/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs @@ -458,6 +458,37 @@ static class C CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } + [Fact] + public void BreakingChange_TypeInference_SpanVsIEnumerable_02_BothSpanAndReadOnlySpan() + { + var source = """ + using System; + using System.Collections.Generic; + + string[] s = new[] { "a" }; + object[] o = s; + + C.R(o); + + static class C + { + public static void R(IEnumerable e) => Console.Write(1); + public static void R(ReadOnlySpan s) => Console.Write(2); + public static void R(Span s) => Console.Write(3); + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13); + CompileAndVerify(comp, expectedOutput: "1").VerifyDiagnostics(); + + var expectedOutput = "2"; + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + [Fact] public void BreakingChange_Conversion_SpanVsIEnumerable() { @@ -518,6 +549,37 @@ static class E CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } + [Fact] + public void BreakingChange_Conversion_SpanVsIEnumerable_BothSpanAndReadOnlySpan() + { + var source = """ + using System; + using System.Collections.Generic; + + string[] s = new[] { "a" }; + object[] o = s; + + o.R(); + + static class E + { + public static void R(this IEnumerable e) => Console.Write(1); + public static void R(this ReadOnlySpan s) => Console.Write(2); + public static void R(this Span s) => Console.Write(3); + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13); + CompileAndVerify(comp, expectedOutput: "1").VerifyDiagnostics(); + + var expectedOutput = "2"; + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + [Fact] public void BreakingChange_Conversion_SpanVsArray() { @@ -631,6 +693,36 @@ static class C CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(expectedDiagnostics); } + [Fact] + public void BreakingChange_OverloadResolution_Betterness_01() + { + var source = """ + using System; + using System.Collections.Generic; + + var x = new int[0]; + C.M(x, x); + + static class C + { + public static void M(IEnumerable a, ReadOnlySpan b) => Console.Write(1); + public static void M(Span a, Span b) => Console.Write(2); + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13); + CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); + + var expectedDiagnostics = new[] + { + // (5,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(IEnumerable, ReadOnlySpan)' and 'C.M(Span, Span)' + // C.M(x, x); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(System.Collections.Generic.IEnumerable, System.ReadOnlySpan)", "C.M(System.Span, System.Span)").WithLocation(5, 3) + }; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(expectedDiagnostics); + } + [Theory, CombinatorialData] public void Conversion_Array_Span_Implicit( [CombinatorialLangVersions] LanguageVersion langVersion, @@ -7857,8 +7949,8 @@ static class C CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } - [Theory, MemberData(nameof(LangVersions))] - public void OverloadResolution_SpanVsReadOnlySpan_01(LanguageVersion langVersion) + [Fact] + public void OverloadResolution_SpanVsReadOnlySpan_01() { var source = """ using System; @@ -7873,8 +7965,16 @@ static class C public static void M(ReadOnlySpan arg) => Console.Write(2); } """; - var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13); CompileAndVerify(comp, expectedOutput: "112").VerifyDiagnostics(); + + var expectedOutput = "212"; + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] @@ -7898,7 +7998,7 @@ static class C var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13); CompileAndVerify(comp, expectedOutput: ExecutionConditionUtil.IsCoreClr ? "1123" : "1121").VerifyDiagnostics(); - var expectedOutput = "1122"; + var expectedOutput = "2122"; comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); @@ -7939,6 +8039,31 @@ static class C CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } + [Fact] + public void OverloadResolution_SpanVsReadOnlySpan_04() + { + var source = """ + using System; + + C.M1(new object[0]); + C.M1(new string[0]); + + C.M2(new object[0]); + C.M2(new string[0]); + + static class C + { + public static void M1(Span arg) => Console.Write(1); + public static void M1(ReadOnlySpan arg) => Console.Write(2); + + public static void M2(Span arg) => Console.Write(1); + public static void M2(ReadOnlySpan arg) => Console.Write(2); + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source); + CompileAndVerify(comp, expectedOutput: "2212").VerifyDiagnostics(); + } + [Fact] public void OverloadResolution_SpanVsReadOnlySpan_ExtensionMethodReceiver_01() { @@ -7958,7 +8083,7 @@ static class C // (new int[0]).E(); Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new int[0]").WithArguments("int[]", "E", "C.E(System.Span)", "System.Span").WithLocation(3, 2)); - var expectedOutput = "1"; + var expectedOutput = "2"; var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); @@ -8009,7 +8134,7 @@ static class C // (new string[0]).E(); Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new object[0]").WithArguments("object[]", "E", "C.E(System.Span)", "System.Span").WithLocation(4, 2)); - var expectedOutput = "21"; + var expectedOutput = "22"; var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); @@ -8050,6 +8175,31 @@ static class C CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } + [Fact] + public void OverloadResolution_SpanVsReadOnlySpan_ExtensionMethodReceiver_05() + { + var source = """ + using System; + + (new object[0]).M1(); + (new string[0]).M1(); + + (new object[0]).M2(); + (new string[0]).M2(); + + static class E + { + public static void M1(this Span arg) => Console.Write(1); + public static void M1(this ReadOnlySpan arg) => Console.Write(2); + + public static void M2(this Span arg) => Console.Write(1); + public static void M2(this ReadOnlySpan arg) => Console.Write(2); + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source); + CompileAndVerify(comp, expectedOutput: "2212").VerifyDiagnostics(); + } + [Fact] public void OverloadResolution_ReadOnlySpanVsReadOnlySpan() { diff --git a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowDiagnosticTests.cs b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowDiagnosticTests.cs index 4a8797b493957..dd184de3140b9 100644 --- a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowDiagnosticTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowDiagnosticTests.cs @@ -858,10 +858,17 @@ public static int goo(int i) return 1; } }"; - var comp = CreateCompilation(program); - var parseErrors = comp.SyntaxTrees[0].GetDiagnostics(); - var errors = comp.GetDiagnostics(); - Assert.Equal(parseErrors.Count(), errors.Count()); + var comp = CreateCompilation(program).VerifyDiagnostics( + // (6,17): error CS0305: Using the generic method 'Program.goo(int)' requires 1 type arguments + // var s = goo<,int>(123); + Diagnostic(ErrorCode.ERR_BadArity, "goo<,int>").WithArguments("Program.goo(int)", "method", "1").WithLocation(6, 17), + // (6,21): error CS1031: Type expected + // var s = goo<,int>(123); + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(6, 21)); + comp.SyntaxTrees[0].GetDiagnostics().Verify( + // (6,21): error CS1031: Type expected + // var s = goo<,int>(123); + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(6, 21)); } [Fact] @@ -964,6 +971,11 @@ struct D { static C> x; } +#pragma warning disable CS0067 // The event 'E.x' is never used +struct E +{ + static event E> x; +} "; CreateCompilation(program) .VerifyDiagnostics( @@ -987,7 +999,13 @@ struct D Diagnostic(ErrorCode.WRN_UnreferencedField, "x").WithArguments("D.x").WithLocation(18, 20), // (14,17): warning CS0169: The field 'C.x' is never used // static D x; - Diagnostic(ErrorCode.WRN_UnreferencedField, "x").WithArguments("C.x").WithLocation(14, 17) + Diagnostic(ErrorCode.WRN_UnreferencedField, "x").WithArguments("C.x").WithLocation(14, 17), + // (23,26): error CS0523: Struct member 'E.x' of type 'E>' causes a cycle in the struct layout + // static event E> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("E.x", "E>").WithLocation(23, 26), + // (23,26): error CS0066: 'E.x': event must be of a delegate type + // static event E> x; + Diagnostic(ErrorCode.ERR_EventNotDelegate, "x").WithArguments("E.x").WithLocation(23, 26) ); } diff --git a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/StructTests.cs b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/StructTests.cs index 32248a37617e9..44c5f04148875 100644 --- a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/StructTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/StructTests.cs @@ -219,6 +219,316 @@ public void GenericStructWithPropertyUsingStruct() Diagnostic(ErrorCode.ERR_StructLayoutCycle, "P").WithArguments("S.P", "S?").WithLocation(3, 13)); } + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + public void InstanceMemberExplosion_01() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct A +{ + A> x; +} + +struct B +{ + A> x; +} + +struct C +{ + D x; +} +struct D +{ + C> x; +} +"; + CreateCompilation(program).VerifyDiagnostics( + // (4,13): error CS0523: Struct member 'A.x' of type 'A>' causes a cycle in the struct layout + // A> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("A.x", "A>").WithLocation(4, 13), + // (14,10): error CS0523: Struct member 'C.x' of type 'D' causes a cycle in the struct layout + // D x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("C.x", "D").WithLocation(14, 10), + // (18,13): error CS0523: Struct member 'D.x' of type 'C>' causes a cycle in the struct layout + // C> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("D.x", "C>").WithLocation(18, 13) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + public void InstanceMemberExplosion_02() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct A +{ + A> x; +} + +struct B +{ + A>> x; +} + +struct C +{ +} +"; + CreateCompilation(program).VerifyDiagnostics( + // (4,13): error CS0523: Struct member 'A.x' of type 'A>' causes a cycle in the struct layout + // A> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("A.x", "A>").WithLocation(4, 13) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + public void InstanceMemberExplosion_03() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct E +{} + +struct X +{ + T _t; +} + +struct Y +{ + X xz; +} + +struct Z +{ + X xe; + X xy; +} +"; + CreateCompilation(program).VerifyDiagnostics( + // (12,10): error CS0523: Struct member 'Y.xz' of type 'X' causes a cycle in the struct layout + // X xz; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "xz").WithArguments("Y.xz", "X").WithLocation(12, 10), + // (18,10): error CS0523: Struct member 'Z.xy' of type 'X' causes a cycle in the struct layout + // X xy; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "xy").WithArguments("Z.xy", "X").WithLocation(18, 10) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + public void InstanceMemberExplosion_04() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct A +{ + A> x; +} + +struct C +{ + C> x; +} + +struct B +{ + A> x; + C> y; + B z; +} + +struct D +{ + B x; +} +"; + CreateCompilation(program).VerifyDiagnostics( + // (4,13): error CS0523: Struct member 'A.x' of type 'A>' causes a cycle in the struct layout + // A> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("A.x", "A>").WithLocation(4, 13), + // (9,13): error CS0523: Struct member 'C.x' of type 'C>' causes a cycle in the struct layout + // C> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("C.x", "C>").WithLocation(9, 13), + // (16,10): error CS0523: Struct member 'B.z' of type 'B' causes a cycle in the struct layout + // B z; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "z").WithArguments("B.z", "B").WithLocation(16, 10) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + public void InstanceMemberExplosion_05() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct A +{ + A> x; +} + +struct C +{ + C> x; +} + +struct B +{ + B z; + A> x; + C> y; +} + +struct D +{ + B x; +} +"; + CreateCompilation(program).VerifyDiagnostics( + // (4,13): error CS0523: Struct member 'A.x' of type 'A>' causes a cycle in the struct layout + // A> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("A.x", "A>").WithLocation(4, 13), + // (9,13): error CS0523: Struct member 'C.x' of type 'C>' causes a cycle in the struct layout + // C> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("C.x", "C>").WithLocation(9, 13), + // (14,10): error CS0523: Struct member 'B.z' of type 'B' causes a cycle in the struct layout + // B z; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "z").WithArguments("B.z", "B").WithLocation(14, 10) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + public void InstanceMemberExplosion_06() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct A +{ + A> x; +} + +struct C +{ + C> x; +} + +struct B +{ + A> x; + B z; + C> y; +} + +struct D +{ + B x; +} +"; + CreateCompilation(program).VerifyDiagnostics( + // (4,13): error CS0523: Struct member 'A.x' of type 'A>' causes a cycle in the struct layout + // A> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("A.x", "A>").WithLocation(4, 13), + // (9,13): error CS0523: Struct member 'C.x' of type 'C>' causes a cycle in the struct layout + // C> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("C.x", "C>").WithLocation(9, 13), + // (15,10): error CS0523: Struct member 'B.z' of type 'B' causes a cycle in the struct layout + // B z; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "z").WithArguments("B.z", "B").WithLocation(15, 10) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + public void StaticMemberExplosion_01() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct A +{ + static A> x; +} + +struct B +{ + static A> x; +} + +struct C +{ + static D x; +} +struct D +{ + static C> x; +} +"; + CreateCompilation(program).VerifyDiagnostics( + // (4,20): error CS0523: Struct member 'A.x' of type 'A>' causes a cycle in the struct layout + // static A> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("A.x", "A>").WithLocation(4, 20), + // (14,17): error CS0523: Struct member 'C.x' of type 'D' causes a cycle in the struct layout + // static D x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("C.x", "D").WithLocation(14, 17), + // (18,20): error CS0523: Struct member 'D.x' of type 'C>' causes a cycle in the struct layout + // static C> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("D.x", "C>").WithLocation(18, 20) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + public void StaticMemberExplosion_02() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct A +{ + static A> x; +} + +struct B +{ + static A>> x; +} + +struct C +{ +} +"; + CreateCompilation(program).VerifyDiagnostics( + // (4,20): error CS0523: Struct member 'A.x' of type 'A>' causes a cycle in the struct layout + // static A> x; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "x").WithArguments("A.x", "A>").WithLocation(4, 20) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66844")] + [WorkItem("https://github.com/dotnet/roslyn/issues/75701")] + public void StaticMemberExplosion_03() + { + string program = @"#pragma warning disable CS0169 // The field is never used +struct E +{} + +struct X +{ + static T _t; +} + +struct Y +{ + static X xz; +} + +struct Z +{ + static X xe; + static X xy; +} +"; + CreateCompilation(program).VerifyDiagnostics( + // Errors are expected here, see https://github.com/dotnet/roslyn/issues/75701. + ); + } + [Fact, WorkItem(1017887, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1017887")] public void EmptyStructsFromMetadata() { diff --git a/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs b/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs index 1eaf022d7af20..903c60708e9c9 100644 --- a/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/OverloadResolutionPriorityTests.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CSharp.UnitTests; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.Test; @@ -2600,4 +2601,223 @@ static void Main() CompileAndVerify([source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "1234"); } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/75871")] + [InlineData(new[] { 1, 2, 3 })] + [InlineData(new[] { 1, 3, 2 })] + [InlineData(new[] { 2, 1, 3 })] + [InlineData(new[] { 2, 3, 1 })] + [InlineData(new[] { 3, 1, 2 })] + [InlineData(new[] { 3, 2, 1 })] + public void ExtensionsOnlyFilteredByApplicability_01(int[] methodOrder) + { + var e2Methods = ""; + + foreach (var method in methodOrder) + { + e2Methods += method switch + { + 1 => """ + [OverloadResolutionPriority(-1)] + public static void R(this int x) => Console.WriteLine("E2.R(int)"); + """, + 2 => """ + public static void R(this string x) => Console.WriteLine("E2.R(string)"); + """, + 3 => """ + public static void R(this bool o) => Console.WriteLine("E2.R(bool)"); + """, + _ => throw ExceptionUtilities.Unreachable(), + }; + } + + var source = $$""" + using System; + using System.Runtime.CompilerServices; + + internal class Program + { + private static void Main(string[] args) + { + int x = 5; + x.R(); // E1.R(int) + } + } + + public static class E1 + { + public static void R(this int x) => Console.WriteLine("E1.R(int)"); + } + + public static class E2 + { + {{e2Methods}} + } + """; + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + comp.VerifyDiagnostics( + // (9,11): error CS0121: The call is ambiguous between the following methods or properties: 'E1.R(int)' and 'E2.R(int)' + // x.R(); // E1.R(int) + Diagnostic(ErrorCode.ERR_AmbigCall, "R").WithArguments("E1.R(int)", "E2.R(int)").WithLocation(9, 11) + ); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/75871")] + [InlineData(new[] { 1, 2, 3 })] + [InlineData(new[] { 1, 3, 2 })] + [InlineData(new[] { 2, 1, 3 })] + [InlineData(new[] { 2, 3, 1 })] + [InlineData(new[] { 3, 1, 2 })] + [InlineData(new[] { 3, 2, 1 })] + public void ExtensionsOnlyFilteredByApplicability_02(int[] methodOrder) + { + var e2Methods = ""; + + foreach (var method in methodOrder) + { + e2Methods += method switch + { + 1 => """ + [OverloadResolutionPriority(-1)] + public static void R(this int x) => Console.WriteLine("E2.R(int)"); + """, + 2 => """ + public static void R(this string x) => Console.WriteLine("E2.R(string)"); + """, + 3 => """ + [OverloadResolutionPriority(-1)] + public static void R(this long o) => Console.WriteLine("E2.R(bool)"); + """, + _ => throw ExceptionUtilities.Unreachable(), + }; + } + + var source = $$""" + using System; + using System.Runtime.CompilerServices; + + internal class Program + { + private static void Main(string[] args) + { + int x = 5; + x.R(); // E1.R(int) + } + } + + public static class E1 + { + public static void R(this int x) => Console.WriteLine("E1.R(int)"); + } + + public static class E2 + { + {{e2Methods}} + } + """; + + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + comp.VerifyDiagnostics( + // (9,11): error CS0121: The call is ambiguous between the following methods or properties: 'E1.R(int)' and 'E2.R(int)' + // x.R(); // E1.R(int) + Diagnostic(ErrorCode.ERR_AmbigCall, "R").WithArguments("E1.R(int)", "E2.R(int)").WithLocation(9, 11) + ); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/75871")] + [InlineData(new[] { 1, 2, 3 })] + [InlineData(new[] { 1, 3, 2 })] + [InlineData(new[] { 2, 1, 3 })] + [InlineData(new[] { 2, 3, 1 })] + [InlineData(new[] { 3, 1, 2 })] + [InlineData(new[] { 3, 2, 1 })] + public void ExtensionsOnlyFilteredByApplicability_03(int[] methodOrder) + { + var e2Methods = ""; + + foreach (var method in methodOrder) + { + e2Methods += method switch + { + 1 => """ + [OverloadResolutionPriority(-1)] + public static void R(this int x) => Console.WriteLine("E2.R(int)"); + """, + 2 => """ + public static void R(this string x) => Console.WriteLine("E2.R(string)"); + """, + 3 => """ + public static void R(this object o) => Console.WriteLine("E2.R(object)"); + """, + _ => throw ExceptionUtilities.Unreachable(), + }; + } + + var source = $$""" + using System; + using System.Runtime.CompilerServices; + + internal class Program + { + private static void Main(string[] args) + { + int x = 5; + x.R(); // E1.R(int) + } + } + + public static class E1 + { + public static void R(this int x) => Console.WriteLine("E1.R(int)"); + } + + public static class E2 + { + {{e2Methods}} + } + """; + + CompileAndVerify([source, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "E1.R(int)").VerifyDiagnostics(); + } + + [Fact] + public void ParamsVsNormal_01() + { + var code = """ + using System; + using System.Runtime.CompilerServices; + + M1(1); + + partial class Program + { + static void M1(int i) => throw null; + [OverloadResolutionPriority(1)] + static void M1(params int[] i) => Console.Write("params"); + } + """; + + CompileAndVerify([code, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "params").VerifyDiagnostics(); + } + + [Fact] + public void ParamsVsNormal_02() + { + var code = """ + using System; + using System.Runtime.CompilerServices; + + M1(1); + + partial class Program + { + [OverloadResolutionPriority(-1)] + static void M1(int i) => throw null; + static void M1(params int[] i) => Console.Write("params"); + } + """; + + CompileAndVerify([code, OverloadResolutionPriorityAttributeDefinition], expectedOutput: "params").VerifyDiagnostics(); + } } diff --git a/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs b/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs index aafcdb5dc3db6..ec891680f6a61 100644 --- a/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs @@ -26475,6 +26475,40 @@ static ref S F2(S x2) } }"; + var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics); + comp.VerifyEmitDiagnostics( + // (12,26): error CS8350: This combination of arguments to 'Program.F1(ref S)' is disallowed because it may expose variables referenced by parameter 'x1' outside of their declaration scope + // ref var y2 = ref F1(ref x2); + Diagnostic(ErrorCode.ERR_CallArgMixing, "F1(ref x2)").WithArguments("Program.F1(ref S)", "x1").WithLocation(12, 26), + // (12,33): error CS8166: Cannot return a parameter by reference 'x2' because it is not a ref parameter + // ref var y2 = ref F1(ref x2); + Diagnostic(ErrorCode.ERR_RefReturnParameter, "x2").WithArguments("x2").WithLocation(12, 33), + // (13,20): error CS8157: Cannot return 'y2' by reference because it was initialized to a value that cannot be returned by reference + // return ref y2; // 1 + Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "y2").WithArguments("y2").WithLocation(13, 20) + ); + } + + [Fact] + public void ReturnRefToByValueParameter_02() + { + var source = +@" +using System.Diagnostics.CodeAnalysis; + +class Program where S : allows ref struct +{ + static ref S F1(ref S x1) + { + return ref x1; + } + static ref S F2(S x2) + { + ref var y2 = ref F1(ref x2); + return ref y2; // 1 + } +}"; + var comp = CreateCompilation(source, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics); comp.VerifyEmitDiagnostics( // (13,20): error CS8157: Cannot return 'y2' by reference because it was initialized to a value that cannot be returned by reference diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs index c2350f829f9eb..eeb7002d0a44e 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs @@ -4639,50 +4639,11 @@ static void Main() expectedOutput: IncludeExpectedOutput("[a, b, c, d], ")); verifier.VerifyIL("Program.GetChars", """ { - // Code size 64 (0x40) - .maxstack 3 - .locals init (int V_0, - char[] V_1, - System.CharEnumerator V_2, - char V_3) + // Code size 11 (0xb) + .maxstack 1 IL_0000: ldstr "abcd" - IL_0005: ldc.i4.0 - IL_0006: stloc.0 - IL_0007: dup - IL_0008: callvirt "int string.Length.get" - IL_000d: newarr "char" - IL_0012: stloc.1 - IL_0013: callvirt "System.CharEnumerator string.GetEnumerator()" - IL_0018: stloc.2 - .try - { - IL_0019: br.s IL_002a - IL_001b: ldloc.2 - IL_001c: callvirt "char System.CharEnumerator.Current.get" - IL_0021: stloc.3 - IL_0022: ldloc.1 - IL_0023: ldloc.0 - IL_0024: ldloc.3 - IL_0025: stelem.i2 - IL_0026: ldloc.0 - IL_0027: ldc.i4.1 - IL_0028: add - IL_0029: stloc.0 - IL_002a: ldloc.2 - IL_002b: callvirt "bool System.CharEnumerator.MoveNext()" - IL_0030: brtrue.s IL_001b - IL_0032: leave.s IL_003e - } - finally - { - IL_0034: ldloc.2 - IL_0035: brfalse.s IL_003d - IL_0037: ldloc.2 - IL_0038: callvirt "void System.IDisposable.Dispose()" - IL_003d: endfinally - } - IL_003e: ldloc.1 - IL_003f: ret + IL_0005: call "char[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_000a: ret } """); } @@ -9454,37 +9415,47 @@ .maxstack 3 ("IEnumerable", "int[]") => """ { - // Code size 31 (0x1f) - .maxstack 3 + // Code size 20 (0x14) + .maxstack 1 .locals init (System.ReadOnlySpan V_0) - IL_0000: newobj "System.Collections.Generic.List..ctor()" - IL_0005: dup - IL_0006: ldarg.0 - IL_0007: callvirt "void System.Collections.Generic.List.AddRange(System.Collections.Generic.IEnumerable)" - IL_000c: callvirt "int[] System.Collections.Generic.List.ToArray()" - IL_0011: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(int[])" - IL_0016: stloc.0 - IL_0017: ldloca.s V_0 - IL_0019: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_001e: ret + IL_0000: ldarg.0 + IL_0001: call "int[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0006: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(int[])" + IL_000b: stloc.0 + IL_000c: ldloca.s V_0 + IL_000e: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0013: ret + } + """, + ("IEnumerable", "Span") => + """ + { + // Code size 25 (0x19) + .maxstack 1 + .locals init (System.ReadOnlySpan V_0) + IL_0000: ldarg.0 + IL_0001: call "int[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0006: newobj "System.Span..ctor(int[])" + IL_000b: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_0010: stloc.0 + IL_0011: ldloca.s V_0 + IL_0013: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0018: ret } """, ("int[]", "int[]") => """ { - // Code size 28 (0x1c) + // Code size 20 (0x14) .maxstack 1 .locals init (System.ReadOnlySpan V_0) IL_0000: ldarg.0 - IL_0001: newobj "System.ReadOnlySpan..ctor(int[])" - IL_0006: stloc.0 - IL_0007: ldloca.s V_0 - IL_0009: call "int[] System.ReadOnlySpan.ToArray()" - IL_000e: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(int[])" - IL_0013: stloc.0 - IL_0014: ldloca.s V_0 - IL_0016: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_001b: ret + IL_0001: call "int[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0006: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(int[])" + IL_000b: stloc.0 + IL_000c: ldloca.s V_0 + IL_000e: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0013: ret } """, ("ReadOnlySpan", "ReadOnlySpan") => @@ -11347,14 +11318,11 @@ static void Main() verifier.VerifyIL("Program.Main", """ { - // Code size 158 (0x9e) + // Code size 96 (0x60) .maxstack 3 .locals init (int V_0, System.Span V_1, - int V_2, - object[] V_3, - System.Collections.Generic.List.Enumerator V_4, - object V_5) + int V_2) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: ldloc.0 @@ -11396,44 +11364,10 @@ .locals init (int V_0, IL_004d: dup IL_004e: ldc.i4.0 IL_004f: call "void CollectionExtensions.Report(object, bool)" - IL_0054: ldc.i4.0 - IL_0055: stloc.2 - IL_0056: dup - IL_0057: callvirt "int System.Collections.Generic.List.Count.get" - IL_005c: newarr "object" - IL_0061: stloc.3 - IL_0062: callvirt "System.Collections.Generic.List.Enumerator System.Collections.Generic.List.GetEnumerator()" - IL_0067: stloc.s V_4 - .try - { - IL_0069: br.s IL_007d - IL_006b: ldloca.s V_4 - IL_006d: call "dynamic System.Collections.Generic.List.Enumerator.Current.get" - IL_0072: stloc.s V_5 - IL_0074: ldloc.3 - IL_0075: ldloc.2 - IL_0076: ldloc.s V_5 - IL_0078: stelem.ref - IL_0079: ldloc.2 - IL_007a: ldc.i4.1 - IL_007b: add - IL_007c: stloc.2 - IL_007d: ldloca.s V_4 - IL_007f: call "bool System.Collections.Generic.List.Enumerator.MoveNext()" - IL_0084: brtrue.s IL_006b - IL_0086: leave.s IL_0096 - } - finally - { - IL_0088: ldloca.s V_4 - IL_008a: constrained. "System.Collections.Generic.List.Enumerator" - IL_0090: callvirt "void System.IDisposable.Dispose()" - IL_0095: endfinally - } - IL_0096: ldloc.3 - IL_0097: ldc.i4.0 - IL_0098: call "void CollectionExtensions.Report(object, bool)" - IL_009d: ret + IL_0054: call "object[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0059: ldc.i4.0 + IL_005a: call "void CollectionExtensions.Report(object, bool)" + IL_005f: ret } """); } @@ -23887,18 +23821,14 @@ .locals init (<>y__InlineArray3 V_0) verifier.VerifyIL("R..ctor(int, T[])", """ { - // Code size 26 (0x1a) + // Code size 18 (0x12) .maxstack 2 - .locals init (System.ReadOnlySpan V_0) IL_0000: ldarg.0 IL_0001: ldarg.2 - IL_0002: newobj "System.ReadOnlySpan..ctor(T[])" - IL_0007: stloc.0 - IL_0008: ldloca.s V_0 - IL_000a: call "T[] System.ReadOnlySpan.ToArray()" - IL_000f: newobj "System.Span..ctor(T[])" - IL_0014: call "R..ctor(scoped System.Span)" - IL_0019: ret + IL_0002: call "T[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0007: newobj "System.Span..ctor(T[])" + IL_000c: call "R..ctor(scoped System.Span)" + IL_0011: ret } """); } @@ -24677,27 +24607,24 @@ static void M(bool b, T[] a) expectedOutput: IncludeExpectedOutput("[1, null, 3], ")); verifier.VerifyIL("Program.M", """ { - // Code size 47 (0x2f) + // Code size 39 (0x27) .maxstack 2 .locals init (System.Span V_0, //s System.ReadOnlySpan V_1) IL_0000: ldloca.s V_0 IL_0002: initobj "System.Span" IL_0008: ldarg.0 - IL_0009: brfalse.s IL_0020 + IL_0009: brfalse.s IL_0018 IL_000b: ldloca.s V_0 IL_000d: ldarg.1 - IL_000e: newobj "System.ReadOnlySpan..ctor(T[])" - IL_0013: stloc.1 - IL_0014: ldloca.s V_1 - IL_0016: call "T[] System.ReadOnlySpan.ToArray()" - IL_001b: call "System.Span..ctor(T[])" - IL_0020: ldloc.0 - IL_0021: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" - IL_0026: stloc.1 - IL_0027: ldloca.s V_1 - IL_0029: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_002e: ret + IL_000e: call "T[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0013: call "System.Span..ctor(T[])" + IL_0018: ldloc.0 + IL_0019: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_001e: stloc.1 + IL_001f: ldloca.s V_1 + IL_0021: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0026: ret } """); } @@ -30396,23 +30323,19 @@ static void Main() verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { - // Code size 47 (0x2f) + // Code size 39 (0x27) .maxstack 3 - .locals init (System.ReadOnlySpan V_0) IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken ".__StaticArrayInitTypeSize=12 .4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" - IL_0011: newobj "System.ReadOnlySpan..ctor(int[])" - IL_0016: stloc.0 - IL_0017: ldloca.s V_0 - IL_0019: call "int[] System.ReadOnlySpan.ToArray()" - IL_001e: call "System.Collections.Immutable.ImmutableArray System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray(int[])" - IL_0023: box "System.Collections.Immutable.ImmutableArray" - IL_0028: ldc.i4.0 - IL_0029: call "void CollectionExtensions.Report(object, bool)" - IL_002e: ret + IL_0011: call "int[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0016: call "System.Collections.Immutable.ImmutableArray System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray(int[])" + IL_001b: box "System.Collections.Immutable.ImmutableArray" + IL_0020: ldc.i4.0 + IL_0021: call "void CollectionExtensions.Report(object, bool)" + IL_0026: ret } """); } @@ -30439,26 +30362,20 @@ static void Main() verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { - // Code size 57 (0x39) + // Code size 44 (0x2c) .maxstack 3 - .locals init (System.Collections.Generic.IEnumerable V_0) //arr IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken ".__StaticArrayInitTypeSize=12 .4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0011: newobj "<>z__ReadOnlyArray..ctor(int[])" - IL_0016: stloc.0 - IL_0017: newobj "System.Collections.Generic.List..ctor()" - IL_001c: dup - IL_001d: ldloc.0 - IL_001e: callvirt "void System.Collections.Generic.List.AddRange(System.Collections.Generic.IEnumerable)" - IL_0023: callvirt "int[] System.Collections.Generic.List.ToArray()" - IL_0028: call "System.Collections.Immutable.ImmutableArray System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray(int[])" - IL_002d: box "System.Collections.Immutable.ImmutableArray" - IL_0032: ldc.i4.0 - IL_0033: call "void CollectionExtensions.Report(object, bool)" - IL_0038: ret + IL_0016: call "int[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_001b: call "System.Collections.Immutable.ImmutableArray System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray(int[])" + IL_0020: box "System.Collections.Immutable.ImmutableArray" + IL_0025: ldc.i4.0 + IL_0026: call "void CollectionExtensions.Report(object, bool)" + IL_002b: ret } """); } @@ -34205,62 +34122,9 @@ .locals init (int[] V_0, """); } - [Fact] - public void ArrayToArray_Covariant_SingleSpread() - { - var source = """ - class Base { } - class Derived : Base { } - - class C - { - static void Main() - { - Base[] array = new Derived[] { new Derived() }; - array.Report(); - - Base[] copy = [..array]; - copy.Report(); - } - } - """; - - var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[Derived], [Derived], "), targetFramework: TargetFramework.Net80); - verifier.VerifyDiagnostics(); - verifier.VerifyIL("C.Main", """ - { - // Code size 57 (0x39) - .maxstack 4 - .locals init (Base[] V_0, - System.ReadOnlySpan V_1) - IL_0000: ldc.i4.1 - IL_0001: newarr "Derived" - IL_0006: dup - IL_0007: ldc.i4.0 - IL_0008: newobj "Derived..ctor()" - IL_000d: stelem.ref - IL_000e: stloc.0 - IL_000f: ldloc.0 - IL_0010: dup - IL_0011: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(Base[])" - IL_0016: stloc.1 - IL_0017: ldloca.s V_1 - IL_0019: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_001e: newobj "System.ReadOnlySpan..ctor(Base[])" - IL_0023: stloc.1 - IL_0024: ldloca.s V_1 - IL_0026: call "Base[] System.ReadOnlySpan.ToArray()" - IL_002b: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(Base[])" - IL_0030: stloc.1 - IL_0031: ldloca.s V_1 - IL_0033: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_0038: ret - } - """); - } - - [Fact] - public void ArrayToArray_Covariant_SingleSpread_ReadOnlySpanCtorMissing() + [Theory] + [CombinatorialData] + public void ArrayToArray_Covariant_SingleSpread(bool missingReadOnlySpanCtor) { var source = """ class Base { } @@ -34279,23 +34143,19 @@ static void Main() } """; - // In the event that the ReadOnlySpan ctor is missing, we do not fall back to converting the array spread value to Span. - // Instead, we lower the spread without optimizing it. var comp = CreateCompilation(new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); + + if (missingReadOnlySpanCtor) + comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[Derived], [Derived], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { - // Code size 90 (0x5a) + // Code size 49 (0x31) .maxstack 4 .locals init (Base[] V_0, - System.ReadOnlySpan V_1, - int V_2, - Base[] V_3, - int V_4, - Base V_5) + System.ReadOnlySpan V_1) IL_0000: ldc.i4.1 IL_0001: newarr "Derived" IL_0006: dup @@ -34309,44 +34169,12 @@ .locals init (Base[] V_0, IL_0016: stloc.1 IL_0017: ldloca.s V_1 IL_0019: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_001e: ldc.i4.0 - IL_001f: stloc.2 - IL_0020: dup - IL_0021: ldlen - IL_0022: conv.i4 - IL_0023: newarr "Base" - IL_0028: stloc.0 - IL_0029: stloc.3 - IL_002a: ldc.i4.0 - IL_002b: stloc.s V_4 - IL_002d: br.s IL_0044 - IL_002f: ldloc.3 - IL_0030: ldloc.s V_4 - IL_0032: ldelem.ref - IL_0033: stloc.s V_5 - IL_0035: ldloc.0 - IL_0036: ldloc.2 - IL_0037: ldloc.s V_5 - IL_0039: stelem.ref - IL_003a: ldloc.2 - IL_003b: ldc.i4.1 - IL_003c: add - IL_003d: stloc.2 - IL_003e: ldloc.s V_4 - IL_0040: ldc.i4.1 - IL_0041: add - IL_0042: stloc.s V_4 - IL_0044: ldloc.s V_4 - IL_0046: ldloc.3 - IL_0047: ldlen - IL_0048: conv.i4 - IL_0049: blt.s IL_002f - IL_004b: ldloc.0 - IL_004c: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(Base[])" - IL_0051: stloc.1 - IL_0052: ldloca.s V_1 - IL_0054: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_0059: ret + IL_001e: call "Base[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0023: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(Base[])" + IL_0028: stloc.1 + IL_0029: ldloca.s V_1 + IL_002b: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0030: ret } """); } @@ -34857,8 +34685,54 @@ .locals init (int V_0, //i """); } + [Theory] + [CombinatorialData] + public void SingleSpread_ArrayToArray_WellKnownMemberMissing_01(bool readOnlySpanMembersMissing) + { + var source = """ + class C + { + static void Main() + { + int[] arr = [1, 2, 3]; + arr.Report(); + int[] arr1 = [..arr]; + arr1.Report(); + } + } + """; + + var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + if (readOnlySpanMembersMissing) + { + comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); + comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ToArray); + } + + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.Main", """ + { + // Code size 36 (0x24) + .maxstack 3 + IL_0000: ldc.i4.3 + IL_0001: newarr "int" + IL_0006: dup + IL_0007: ldtoken ".__StaticArrayInitTypeSize=12 .4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" + IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + IL_0011: dup + IL_0012: ldc.i4.0 + IL_0013: call "void CollectionExtensions.Report(object, bool)" + IL_0018: call "int[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_001d: ldc.i4.0 + IL_001e: call "void CollectionExtensions.Report(object, bool)" + IL_0023: ret + } + """); + } + [Fact] - public void SingleSpread_WellKnownMemberMissing() + public void SingleSpread_ArrayToArray_WellKnownMemberMissing_02() { var source = """ class C @@ -34874,6 +34748,7 @@ static void Main() """; var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.MakeMemberMissing(WellKnownMember.System_Linq_Enumerable__ToArray); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); verifier.VerifyDiagnostics(); @@ -34899,84 +34774,40 @@ .locals init (System.ReadOnlySpan V_0) IL_002b: ret } """); + } + + [Fact] + public void SingleSpread_ArrayToArray_WellKnownMemberMissing_03() + { + var source = """ + class C + { + static void Main() + { + int[] arr = [1, 2, 3]; + arr.Report(); + int[] arr1 = [..arr]; + arr1.Report(); + } + } + """; - // No ReadOnlySpan(T[]) constructor. Spread optimizations can't be performed. - comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.MakeMemberMissing(WellKnownMember.System_Linq_Enumerable__ToArray); comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); + comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ToArray); - verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { - // Code size 72 (0x48) - .maxstack 3 - .locals init (int V_0, + // Code size 72 (0x48) + .maxstack 3 + .locals init (int V_0, int[] V_1, int[] V_2, int V_3, int V_4) - IL_0000: ldc.i4.3 - IL_0001: newarr "int" - IL_0006: dup - IL_0007: ldtoken ".__StaticArrayInitTypeSize=12 .4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" - IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" - IL_0011: dup - IL_0012: ldc.i4.0 - IL_0013: call "void CollectionExtensions.Report(object, bool)" - IL_0018: ldc.i4.0 - IL_0019: stloc.0 - IL_001a: dup - IL_001b: ldlen - IL_001c: conv.i4 - IL_001d: newarr "int" - IL_0022: stloc.1 - IL_0023: stloc.2 - IL_0024: ldc.i4.0 - IL_0025: stloc.3 - IL_0026: br.s IL_003a - IL_0028: ldloc.2 - IL_0029: ldloc.3 - IL_002a: ldelem.i4 - IL_002b: stloc.s V_4 - IL_002d: ldloc.1 - IL_002e: ldloc.0 - IL_002f: ldloc.s V_4 - IL_0031: stelem.i4 - IL_0032: ldloc.0 - IL_0033: ldc.i4.1 - IL_0034: add - IL_0035: stloc.0 - IL_0036: ldloc.3 - IL_0037: ldc.i4.1 - IL_0038: add - IL_0039: stloc.3 - IL_003a: ldloc.3 - IL_003b: ldloc.2 - IL_003c: ldlen - IL_003d: conv.i4 - IL_003e: blt.s IL_0028 - IL_0040: ldloc.1 - IL_0041: ldc.i4.0 - IL_0042: call "void CollectionExtensions.Report(object, bool)" - IL_0047: ret - } - """); - - // No ReadOnlySpan.ToArray method. ToArray optimization for single spreads cannot be performed, but CopyTo optimization still can. - comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ToArray); - - verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); - verifier.VerifyDiagnostics(); - verifier.VerifyIL("C.Main", """ - { - // Code size 92 (0x5c) - .maxstack 4 - .locals init (int[] V_0, - int V_1, - int[] V_2, - System.ReadOnlySpan V_3, - System.Span V_4) IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup @@ -34985,53 +34816,436 @@ .locals init (int[] V_0, IL_0011: dup IL_0012: ldc.i4.0 IL_0013: call "void CollectionExtensions.Report(object, bool)" - IL_0018: stloc.0 - IL_0019: ldc.i4.0 - IL_001a: stloc.1 - IL_001b: ldloc.0 - IL_001c: ldlen - IL_001d: conv.i4 - IL_001e: newarr "int" + IL_0018: ldc.i4.0 + IL_0019: stloc.0 + IL_001a: dup + IL_001b: ldlen + IL_001c: conv.i4 + IL_001d: newarr "int" + IL_0022: stloc.1 IL_0023: stloc.2 - IL_0024: ldloca.s V_3 - IL_0026: ldloc.0 - IL_0027: call "System.ReadOnlySpan..ctor(int[])" - IL_002c: ldloca.s V_3 - IL_002e: ldloc.2 - IL_002f: newobj "System.Span..ctor(int[])" - IL_0034: stloc.s V_4 - IL_0036: ldloca.s V_4 - IL_0038: ldloc.1 - IL_0039: ldloca.s V_3 - IL_003b: call "int System.ReadOnlySpan.Length.get" - IL_0040: call "System.Span System.Span.Slice(int, int)" - IL_0045: call "void System.ReadOnlySpan.CopyTo(System.Span)" - IL_004a: ldloc.1 - IL_004b: ldloca.s V_3 - IL_004d: call "int System.ReadOnlySpan.Length.get" - IL_0052: add - IL_0053: stloc.1 - IL_0054: ldloc.2 - IL_0055: ldc.i4.0 - IL_0056: call "void CollectionExtensions.Report(object, bool)" - IL_005b: ret + IL_0024: ldc.i4.0 + IL_0025: stloc.3 + IL_0026: br.s IL_003a + IL_0028: ldloc.2 + IL_0029: ldloc.3 + IL_002a: ldelem.i4 + IL_002b: stloc.s V_4 + IL_002d: ldloc.1 + IL_002e: ldloc.0 + IL_002f: ldloc.s V_4 + IL_0031: stelem.i4 + IL_0032: ldloc.0 + IL_0033: ldc.i4.1 + IL_0034: add + IL_0035: stloc.0 + IL_0036: ldloc.3 + IL_0037: ldc.i4.1 + IL_0038: add + IL_0039: stloc.3 + IL_003a: ldloc.3 + IL_003b: ldloc.2 + IL_003c: ldlen + IL_003d: conv.i4 + IL_003e: blt.s IL_0028 + IL_0040: ldloc.1 + IL_0041: ldc.i4.0 + IL_0042: call "void CollectionExtensions.Report(object, bool)" + IL_0047: ret } """); } [Fact] - public void MultipleSpreads_WellKnownMemberMissing() + public void SingleSpread_ArrayToArray_Covariant() { var source = """ + using System; + + Console.Write(C.M(["a"])[0]); + class C { - static void Main() - { - int[] arr = [1, 2]; - arr.Report(); - int[] arr1 = [..arr, ..arr]; - arr1.Report(); - } + public static object[] M(string[] arr) => [..arr]; + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "a"); + verifier.VerifyIL("C.M", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call "object[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0006: ret + } + """); + } + + [Fact] + public void SingleSpread_ArrayToSpan_CovariantVersusInvariant() + { + var source = """ + using System; + + string[] arr = ["a"]; + C.M1(arr); + C.M2(arr); + + class C + { + public static void M1(string[] arr) + { + Span span = [..arr]; + span.Report(); + } + public static void M2(object[] arr) + { + Span span = [..arr]; + span.Report(); + } + } + """; + + var verifier = CompileAndVerify([source, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput("[a], [a], "), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + var expectedIL = """ + { + // Code size 25 (0x19) + .maxstack 1 + .locals init (System.ReadOnlySpan V_0) + IL_0000: ldarg.0 + IL_0001: call "object[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0006: newobj "System.Span..ctor(object[])" + IL_000b: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_0010: stloc.0 + IL_0011: ldloca.s V_0 + IL_0013: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0018: ret + } + """; + verifier.VerifyIL("C.M1", expectedIL); + verifier.VerifyIL("C.M2", expectedIL); + } + + [Fact] + public void SingleSpread_ArrayToReadOnlySpan_CovariantVersusInvariant() + { + var source = """ + using System; + + string[] arr = ["a"]; + C.M1(arr); + C.M2(arr); + + class C + { + public static void M1(string[] arr) + { + ReadOnlySpan span = [..arr]; + span.Report(); + } + public static void M2(object[] arr) + { + ReadOnlySpan span = [..arr]; + span.Report(); + } + } + """; + + var verifier = CompileAndVerify([source, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput("[a], [a], "), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + var expectedIL = """ + { + // Code size 21 (0x15) + .maxstack 2 + .locals init (System.ReadOnlySpan V_0) //span + IL_0000: ldloca.s V_0 + IL_0002: ldarg.0 + IL_0003: call "object[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0008: call "System.ReadOnlySpan..ctor(object[])" + IL_000d: ldloca.s V_0 + IL_000f: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0014: ret + } + """; + verifier.VerifyIL("C.M1", expectedIL); + verifier.VerifyIL("C.M2", expectedIL); + } + + [Fact] + public void SingleSpread_ListToArray_Covariant() + { + // Linq ToArray method is applicable, but we do not use it in this case, + // because the List has a struct enumerator and doesn't implement ICollection. + // If we could get a Span out of the List, then covariant-convert, we could use ReadOnlySpan.ToArray() here. + // https://github.com/dotnet/roslyn/issues/71106 + var source = """ + using System; + using System.Collections.Generic; + + Console.Write(C.M(["a"])[0]); + + class C + { + public static object[] M(List list) => [..list]; + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "a"); + verifier.VerifyIL("C.M", """ + { + // Code size 66 (0x42) + .maxstack 3 + .locals init (int V_0, + object[] V_1, + System.Collections.Generic.List.Enumerator V_2, + string V_3) + IL_0000: ldarg.0 + IL_0001: ldc.i4.0 + IL_0002: stloc.0 + IL_0003: dup + IL_0004: callvirt "int System.Collections.Generic.List.Count.get" + IL_0009: newarr "object" + IL_000e: stloc.1 + IL_000f: callvirt "System.Collections.Generic.List.Enumerator System.Collections.Generic.List.GetEnumerator()" + IL_0014: stloc.2 + .try + { + IL_0015: br.s IL_0027 + IL_0017: ldloca.s V_2 + IL_0019: call "string System.Collections.Generic.List.Enumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldloc.1 + IL_0020: ldloc.0 + IL_0021: ldloc.3 + IL_0022: stelem.ref + IL_0023: ldloc.0 + IL_0024: ldc.i4.1 + IL_0025: add + IL_0026: stloc.0 + IL_0027: ldloca.s V_2 + IL_0029: call "bool System.Collections.Generic.List.Enumerator.MoveNext()" + IL_002e: brtrue.s IL_0017 + IL_0030: leave.s IL_0040 + } + finally + { + IL_0032: ldloca.s V_2 + IL_0034: constrained. "System.Collections.Generic.List.Enumerator" + IL_003a: callvirt "void System.IDisposable.Dispose()" + IL_003f: endfinally + } + IL_0040: ldloc.1 + IL_0041: ret + } + """); + } + + [Fact] + public void SingleSpread_ListToIEnumerable_Covariant() + { + var source = """ + using System; + using System.Collections.Generic; + + foreach (var item in C.M(["a"])) + Console.Write(item); + + class C + { + public static IEnumerable M(List list) => [..list]; + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "a"); + verifier.VerifyIL("C.M", """ + { + // Code size 71 (0x47) + .maxstack 3 + .locals init (int V_0, + object[] V_1, + System.Collections.Generic.List.Enumerator V_2, + string V_3) + IL_0000: ldarg.0 + IL_0001: ldc.i4.0 + IL_0002: stloc.0 + IL_0003: dup + IL_0004: callvirt "int System.Collections.Generic.List.Count.get" + IL_0009: newarr "object" + IL_000e: stloc.1 + IL_000f: callvirt "System.Collections.Generic.List.Enumerator System.Collections.Generic.List.GetEnumerator()" + IL_0014: stloc.2 + .try + { + IL_0015: br.s IL_0027 + IL_0017: ldloca.s V_2 + IL_0019: call "string System.Collections.Generic.List.Enumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldloc.1 + IL_0020: ldloc.0 + IL_0021: ldloc.3 + IL_0022: stelem.ref + IL_0023: ldloc.0 + IL_0024: ldc.i4.1 + IL_0025: add + IL_0026: stloc.0 + IL_0027: ldloca.s V_2 + IL_0029: call "bool System.Collections.Generic.List.Enumerator.MoveNext()" + IL_002e: brtrue.s IL_0017 + IL_0030: leave.s IL_0040 + } + finally + { + IL_0032: ldloca.s V_2 + IL_0034: constrained. "System.Collections.Generic.List.Enumerator" + IL_003a: callvirt "void System.IDisposable.Dispose()" + IL_003f: endfinally + } + IL_0040: ldloc.1 + IL_0041: newobj "<>z__ReadOnlyArray..ctor(object[])" + IL_0046: ret + } + """); + } + + [Fact] + public void SingleSpread_ArrayToArray_Boxing() + { + var source = """ + using System; + + Console.Write(C.M([1])[0]); + + class C + { + public static object[] M(int[] arr) => [..arr]; + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "1"); + verifier.VerifyIL("C.M", """ + { + // Code size 48 (0x30) + .maxstack 3 + .locals init (int V_0, + object[] V_1, + int[] V_2, + int V_3, + int V_4) + IL_0000: ldarg.0 + IL_0001: ldc.i4.0 + IL_0002: stloc.0 + IL_0003: dup + IL_0004: ldlen + IL_0005: conv.i4 + IL_0006: newarr "object" + IL_000b: stloc.1 + IL_000c: stloc.2 + IL_000d: ldc.i4.0 + IL_000e: stloc.3 + IL_000f: br.s IL_0028 + IL_0011: ldloc.2 + IL_0012: ldloc.3 + IL_0013: ldelem.i4 + IL_0014: stloc.s V_4 + IL_0016: ldloc.1 + IL_0017: ldloc.0 + IL_0018: ldloc.s V_4 + IL_001a: box "int" + IL_001f: stelem.ref + IL_0020: ldloc.0 + IL_0021: ldc.i4.1 + IL_0022: add + IL_0023: stloc.0 + IL_0024: ldloc.3 + IL_0025: ldc.i4.1 + IL_0026: add + IL_0027: stloc.3 + IL_0028: ldloc.3 + IL_0029: ldloc.2 + IL_002a: ldlen + IL_002b: conv.i4 + IL_002c: blt.s IL_0011 + IL_002e: ldloc.1 + IL_002f: ret + } + """); + } + + [Fact] + public void SingleSpread_ArrayToIEnumerable_Covariant() + { + var source = """ + using System; + using System.Collections.Generic; + + foreach (var item in C.M(["a"])) + Console.Write(item); + + class C + { + public static IEnumerable M(string[] arr) => [..arr]; + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "a"); + verifier.VerifyIL("C.M", """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call "object[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0006: newobj "<>z__ReadOnlyArray..ctor(object[])" + IL_000b: ret + } + """); + } + + [Fact] + public void SingleSpread_IEnumerableToIEnumerable_Covariant() + { + var source = """ + using System; + using System.Collections.Generic; + + foreach (var item in C.M(["a"])) + Console.Write(item); + + class C + { + public static IEnumerable M(IEnumerable enumerable) => [..enumerable]; + } + """; + + // Note: We could use the Linq ToArray method here and save an allocation compared to making a List. + // However, it's not that significant, since the current codegen doesn't redundantly copy the elements themselves. + var verifier = CompileAndVerify(source, expectedOutput: "a"); + verifier.VerifyIL("C.M", """ + { + // Code size 18 (0x12) + .maxstack 3 + IL_0000: newobj "System.Collections.Generic.List..ctor()" + IL_0005: dup + IL_0006: ldarg.0 + IL_0007: callvirt "void System.Collections.Generic.List.AddRange(System.Collections.Generic.IEnumerable)" + IL_000c: newobj "<>z__ReadOnlyList..ctor(System.Collections.Generic.List)" + IL_0011: ret + } + """); + } + + [Fact] + public void MultipleSpreads_WellKnownMemberMissing() + { + var source = """ + class C + { + static void Main() + { + int[] arr = [1, 2]; + arr.Report(); + int[] arr1 = [..arr, ..arr]; + arr1.Report(); + } } """; @@ -35547,7 +35761,7 @@ static void Main() verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { - // Code size 65 (0x41) + // Code size 57 (0x39) .maxstack 3 .locals init (int[] V_0, //arr System.ReadOnlySpan V_1) @@ -35563,16 +35777,13 @@ .locals init (int[] V_0, //arr IL_0019: ldloca.s V_1 IL_001b: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" IL_0020: ldloc.0 - IL_0021: newobj "System.ReadOnlySpan..ctor(int[])" - IL_0026: stloc.1 - IL_0027: ldloca.s V_1 - IL_0029: call "int[] System.ReadOnlySpan.ToArray()" - IL_002e: newobj "System.Span..ctor(int[])" - IL_0033: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" - IL_0038: stloc.1 - IL_0039: ldloca.s V_1 - IL_003b: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_0040: ret + IL_0021: call "int[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0026: newobj "System.Span..ctor(int[])" + IL_002b: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_0030: stloc.1 + IL_0031: ldloca.s V_1 + IL_0033: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" + IL_0038: ret } """); } @@ -35598,7 +35809,7 @@ static void Main() verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { - // Code size 56 (0x38) + // Code size 48 (0x30) .maxstack 3 .locals init (System.ReadOnlySpan V_0) IL_0000: ldc.i4.3 @@ -35611,14 +35822,11 @@ .locals init (System.ReadOnlySpan V_0) IL_0017: stloc.0 IL_0018: ldloca.s V_0 IL_001a: call "void CollectionExtensions.Report(in System.ReadOnlySpan)" - IL_001f: newobj "System.ReadOnlySpan..ctor(int[])" - IL_0024: stloc.0 - IL_0025: ldloca.s V_0 - IL_0027: call "int[] System.ReadOnlySpan.ToArray()" - IL_002c: newobj "<>z__ReadOnlyArray..ctor(int[])" - IL_0031: ldc.i4.0 - IL_0032: call "void CollectionExtensions.Report(object, bool)" - IL_0037: ret + IL_001f: call "int[] System.Linq.Enumerable.ToArray(System.Collections.Generic.IEnumerable)" + IL_0024: newobj "<>z__ReadOnlyArray..ctor(int[])" + IL_0029: ldc.i4.0 + IL_002a: call "void CollectionExtensions.Report(object, bool)" + IL_002f: ret } """); } @@ -36613,6 +36821,226 @@ .locals init (System.Collections.Generic.List V_0, """); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] + public void Array_SingleSpread_CustomCollection_NotICollectionAndStructEnumerator() + { + var source = """ + using System.Collections; + using System.Collections.Generic; + + class MyCollection(List list) : IEnumerable + { + public Enumerator GetEnumerator() => new(list.GetEnumerator()); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public int Count => list.Count; + + public struct Enumerator(List.Enumerator enumerator) : IEnumerator + { + public int Current => enumerator.Current; + + object IEnumerator.Current => Current; + + public bool MoveNext() => enumerator.MoveNext(); + + public void Dispose() => enumerator.Dispose(); + + public void Reset() { } + } + } + + class C + { + static void Main() + { + M(new([1, 2, 3])).Report(); + } + + static int[] M(MyCollection c) => [..c]; + } + """; + + var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3],", verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("C.M", """ + { + // Code size 66 (0x42) + .maxstack 3 + .locals init (int V_0, + int[] V_1, + MyCollection.Enumerator V_2, + int V_3) + IL_0000: ldarg.0 + IL_0001: ldc.i4.0 + IL_0002: stloc.0 + IL_0003: dup + IL_0004: callvirt "int MyCollection.Count.get" + IL_0009: newarr "int" + IL_000e: stloc.1 + IL_000f: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" + IL_0014: stloc.2 + .try + { + IL_0015: br.s IL_0027 + IL_0017: ldloca.s V_2 + IL_0019: call "int MyCollection.Enumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldloc.1 + IL_0020: ldloc.0 + IL_0021: ldloc.3 + IL_0022: stelem.i4 + IL_0023: ldloc.0 + IL_0024: ldc.i4.1 + IL_0025: add + IL_0026: stloc.0 + IL_0027: ldloca.s V_2 + IL_0029: call "bool MyCollection.Enumerator.MoveNext()" + IL_002e: brtrue.s IL_0017 + IL_0030: leave.s IL_0040 + } + finally + { + IL_0032: ldloca.s V_2 + IL_0034: constrained. "MyCollection.Enumerator" + IL_003a: callvirt "void System.IDisposable.Dispose()" + IL_003f: endfinally + } + IL_0040: ldloc.1 + IL_0041: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] + public void Array_SingleSpread_CustomCollection_NotICollectionAndStructEnumerator_NoCountProperty() + { + var source = """ + using System.Collections; + using System.Collections.Generic; + + class MyCollection(List list) : IEnumerable + { + public Enumerator GetEnumerator() => new(list.GetEnumerator()); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public struct Enumerator(List.Enumerator enumerator) : IEnumerator + { + public int Current => enumerator.Current; + + object IEnumerator.Current => Current; + + public bool MoveNext() => enumerator.MoveNext(); + + public void Dispose() => enumerator.Dispose(); + + public void Reset() { } + } + } + + class C + { + static void Main() + { + M(new([1, 2, 3])).Report(); + } + + static int[] M(MyCollection c) => [..c]; + } + """; + + var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3],", verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("C.M", """ + { + // Code size 62 (0x3e) + .maxstack 2 + .locals init (System.Collections.Generic.List V_0, + MyCollection.Enumerator V_1, + int V_2) + IL_0000: newobj "System.Collections.Generic.List..ctor()" + IL_0005: stloc.0 + IL_0006: ldarg.0 + IL_0007: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_001e + IL_000f: ldloca.s V_1 + IL_0011: call "int MyCollection.Enumerator.Current.get" + IL_0016: stloc.2 + IL_0017: ldloc.0 + IL_0018: ldloc.2 + IL_0019: callvirt "void System.Collections.Generic.List.Add(int)" + IL_001e: ldloca.s V_1 + IL_0020: call "bool MyCollection.Enumerator.MoveNext()" + IL_0025: brtrue.s IL_000f + IL_0027: leave.s IL_0037 + } + finally + { + IL_0029: ldloca.s V_1 + IL_002b: constrained. "MyCollection.Enumerator" + IL_0031: callvirt "void System.IDisposable.Dispose()" + IL_0036: endfinally + } + IL_0037: ldloc.0 + IL_0038: callvirt "int[] System.Collections.Generic.List.ToArray()" + IL_003d: ret + } + """); + } + + [Fact] + public void SingleSpread_ToArray_MustLowerSpreadOperand() + { + var source = """ + using System; + using System.Collections.Generic; + + class C + { + static Action[] M1() + { + return [..new[] { () => Console.Write(1) }]; + } + + static Action[] M2() + { + return [..new List { () => Console.Write(2) }]; + } + + static Action[] M3() + { + return [..new Span(new[] { () => Console.Write(3) })]; + } + + static Action[] M4() + { + return [..new ReadOnlySpan(new[] { () => Console.Write(4) })]; + } + + static void Main() + { + M1()[0](); + M2()[0](); + M3()[0](); + M4()[0](); + } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: IncludeExpectedOutput("1234"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] public void List_SingleSpread_CustomCollection_NotICollectionAndStructEnumerator_MissingICollectionOfTType() { diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs index 9ee3226572928..5c1212d8e9755 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs @@ -20954,6 +20954,9 @@ private static void AssertContainedInDeclaratorArguments(DeclarationExpressionSy Assert.True(decl.Ancestors().OfType().First().ArgumentList.Contains(decl)); } + private static void AssertNotContainedInDeclaratorArguments(DeclarationExpressionSyntax decl) + => Assert.Empty(decl.Ancestors().OfType()); + private static void AssertContainedInDeclaratorArguments(params DeclarationExpressionSyntax[] decls) { foreach (var decl in decls) @@ -21562,48 +21565,81 @@ static bool TakeOutParam(object y, out bool x) } "; var compilation = CreateCompilationWithMscorlib461(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular); - int[] exclude = new int[] { (int)ErrorCode.ERR_BadVarDecl, - (int)ErrorCode.ERR_SyntaxError, - (int)ErrorCode.ERR_UnexpectedToken, - (int)ErrorCode.WRN_UnreferencedVar, - (int)ErrorCode.ERR_UseDefViolation - }; + int[] exclude = [ + (int)ErrorCode.ERR_BadVarDecl, + (int)ErrorCode.ERR_SyntaxError, + (int)ErrorCode.ERR_UnexpectedToken, + (int)ErrorCode.WRN_UnreferencedVar, + (int)ErrorCode.ERR_UseDefViolation, + (int)ErrorCode.ERR_SemicolonExpected, + (int)ErrorCode.ERR_CloseParenExpected, + (int)ErrorCode.ERR_InvalidExprTerm, + ]; compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( - // (109,13): error CS1023: Embedded statement cannot be a declaration or labeled statement - // var y12 = 12; - Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "var y12 = 12;").WithLocation(109, 13), + // (12,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(12, 22), + // (22,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(22, 22), + // (33,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(33, 22), // (34,47): error CS0136: A local or parameter named 'x4' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(TakeOutParam(true, out var x4) && x4) Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x4").WithArguments("x4").WithLocation(34, 47), + // (41,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(41, 22), // (42,20): error CS0841: Cannot use local variable 'x6' before it is declared // Dummy(x6 && TakeOutParam(true, out var x6)) Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x6").WithArguments("x6").WithLocation(42, 20), + // (49,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(49, 22), // (53,17): error CS0136: A local or parameter named 'x7' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // var x7 = 12; Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x7").WithArguments("x7").WithLocation(53, 17), + // (60,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(60, 22), // (65,34): error CS0103: The name 'x8' does not exist in the current context // System.Console.WriteLine(x8); Diagnostic(ErrorCode.ERR_NameNotInContext, "x8").WithArguments("x8").WithLocation(65, 34), - // (65,9): warning CS0162: Unreachable code detected - // System.Console.WriteLine(x8); - Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(65, 9), + // (70,23): error CS0103: The name 'b1' does not exist in the current context + // for (bool a1, b1( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b1").WithArguments("b1").WithLocation(70, 23), + // (75,27): error CS0103: The name 'b2' does not exist in the current context + // for (bool a2, b2( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b2").WithArguments("b2").WithLocation(75, 27), // (76,51): error CS0136: A local or parameter named 'x9' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(TakeOutParam(true, out var x9) && x9) // 2 Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(76, 51), + // (84,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(84, 22), // (85,33): error CS0103: The name 'y10' does not exist in the current context // Dummy(TakeOutParam(y10, out var x10)) Diagnostic(ErrorCode.ERR_NameNotInContext, "y10").WithArguments("y10").WithLocation(85, 33), + // (106,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(106, 22), // (107,33): error CS0103: The name 'y12' does not exist in the current context // Dummy(TakeOutParam(y12, out var x12)) Diagnostic(ErrorCode.ERR_NameNotInContext, "y12").WithArguments("y12").WithLocation(107, 33), + // (109,13): error CS1023: Embedded statement cannot be a declaration or labeled statement + // var y12 = 12; + Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "var y12 = 12;").WithLocation(109, 13), // (109,17): warning CS0219: The variable 'y12' is assigned but its value is never used // var y12 = 12; Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "y12").WithArguments("y12").WithLocation(109, 17), - // (124,44): error CS0128: A local variable named 'x14' is already defined in this scope + // (122,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(122, 22), + // (124,44): error CS0128: A local variable or function named 'x14' is already defined in this scope // TakeOutParam(2, out var x14), - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x14").WithArguments("x14").WithLocation(124, 44) - ); + Diagnostic(ErrorCode.ERR_LocalDuplicate, "x14").WithArguments("x14").WithLocation(124, 44)); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree); @@ -21611,39 +21647,39 @@ static bool TakeOutParam(object y, out bool x) var x1Decl = GetOutVarDeclarations(tree, "x1").Single(); var x1Ref = GetReferences(tree, "x1").ToArray(); Assert.Equal(2, x1Ref.Length); - AssertContainedInDeclaratorArguments(x1Decl); + AssertNotContainedInDeclaratorArguments(x1Decl); VerifyModelForOutVarWithoutDataFlow(model, x1Decl, x1Ref); var x2Decl = GetOutVarDeclarations(tree, "x2").Single(); var x2Ref = GetReferences(tree, "x2").ToArray(); Assert.Equal(2, x2Ref.Length); - AssertContainedInDeclaratorArguments(x2Decl); + AssertNotContainedInDeclaratorArguments(x2Decl); VerifyModelForOutVarWithoutDataFlow(model, x2Decl, x2Ref); var x4Decl = GetOutVarDeclarations(tree, "x4").Single(); var x4Ref = GetReferences(tree, "x4").ToArray(); Assert.Equal(3, x4Ref.Length); - AssertContainedInDeclaratorArguments(x4Decl); + AssertNotContainedInDeclaratorArguments(x4Decl); VerifyNotAnOutLocal(model, x4Ref[0]); VerifyModelForOutVarWithoutDataFlow(model, x4Decl, x4Ref[1], x4Ref[2]); var x6Decl = GetOutVarDeclarations(tree, "x6").Single(); var x6Ref = GetReferences(tree, "x6").ToArray(); Assert.Equal(2, x6Ref.Length); - AssertContainedInDeclaratorArguments(x6Decl); + AssertNotContainedInDeclaratorArguments(x6Decl); VerifyModelForOutVarWithoutDataFlow(model, x6Decl, x6Ref); var x7Decl = GetOutVarDeclarations(tree, "x7").Single(); var x7Ref = GetReferences(tree, "x7").ToArray(); Assert.Equal(2, x7Ref.Length); - AssertContainedInDeclaratorArguments(x7Decl); + AssertNotContainedInDeclaratorArguments(x7Decl); VerifyModelForOutVarWithoutDataFlow(model, x7Decl, x7Ref[0]); VerifyNotAnOutLocal(model, x7Ref[1]); var x8Decl = GetOutVarDeclarations(tree, "x8").Single(); var x8Ref = GetReferences(tree, "x8").ToArray(); Assert.Equal(3, x8Ref.Length); - AssertContainedInDeclaratorArguments(x8Decl); + AssertNotContainedInDeclaratorArguments(x8Decl); VerifyModelForOutVarWithoutDataFlow(model, x8Decl, x8Ref[0], x8Ref[1]); VerifyNotInScope(model, x8Ref[2]); @@ -21651,7 +21687,7 @@ static bool TakeOutParam(object y, out bool x) var x9Ref = GetReferences(tree, "x9").ToArray(); Assert.Equal(2, x9Decl.Length); Assert.Equal(4, x9Ref.Length); - AssertContainedInDeclaratorArguments(x9Decl); + Assert.All(x9Decl, x9Decl => AssertNotContainedInDeclaratorArguments(x9Decl)); VerifyModelForOutVarWithoutDataFlow(model, x9Decl[0], x9Ref[0], x9Ref[1]); VerifyModelForOutVarWithoutDataFlow(model, x9Decl[1], x9Ref[2], x9Ref[3]); @@ -21667,7 +21703,7 @@ static bool TakeOutParam(object y, out bool x) var x14Ref = GetReferences(tree, "x14").ToArray(); Assert.Equal(2, x14Decl.Length); Assert.Equal(2, x14Ref.Length); - AssertContainedInDeclaratorArguments(x14Decl); + Assert.All(x14Decl, x14Decl => AssertNotContainedInDeclaratorArguments(x14Decl)); VerifyModelForOutVarWithoutDataFlow(model, x14Decl[0], x14Ref); VerifyModelForOutVarDuplicateInSameScope(model, x14Decl[1]); } @@ -21727,77 +21763,89 @@ static bool TakeOutParam(object y, out bool x) } "; var compilation = CreateCompilationWithMscorlib461(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular); - int[] exclude = new int[] { (int)ErrorCode.ERR_BadVarDecl, - (int)ErrorCode.ERR_SyntaxError, - (int)ErrorCode.ERR_UnexpectedToken, - (int)ErrorCode.WRN_UnreferencedVar, - (int)ErrorCode.ERR_CloseParenExpected - }; + int[] exclude = [ + (int)ErrorCode.ERR_BadVarDecl, + (int)ErrorCode.ERR_SyntaxError, + (int)ErrorCode.ERR_UnexpectedToken, + (int)ErrorCode.WRN_UnreferencedVar, + (int)ErrorCode.ERR_CloseParenExpected, + (int)ErrorCode.ERR_SemicolonExpected, + (int)ErrorCode.ERR_InvalidExprTerm, + ]; compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( - // (13,47): error CS0128: A local variable or function named 'x4' is already defined in this scope - // Dummy(TakeOutParam(true, out var x4) && x4) - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x4").WithArguments("x4").WithLocation(13, 47), - // (21,47): error CS0128: A local variable or function named 'x7' is already defined in this scope + // (12,22): error CS0841: Cannot use local variable 'x4' before it is declared + // for (bool d, x4( + Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x4").WithArguments("x4").WithLocation(12, 22), + // (20,30): error CS0103: The name 'b' does not exist in the current context + // for (bool x7 = true, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(20, 30), + // (21,47): error CS0136: A local or parameter named 'x7' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(TakeOutParam(true, out var x7) && x7) - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x7").WithArguments("x7").WithLocation(21, 47), + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x7").WithArguments("x7").WithLocation(21, 47), // (20,19): warning CS0219: The variable 'x7' is assigned but its value is never used // for (bool x7 = true, b( Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x7").WithArguments("x7").WithLocation(20, 19), - // (29,52): error CS0128: A local variable or function named 'x8' is already defined in this scope + // (28,21): error CS0103: The name 'b1' does not exist in the current context + // for (bool d,b1(Dummy(TakeOutParam(true, out var x8) && x8)], + Diagnostic(ErrorCode.ERR_NameNotInContext, "b1").WithArguments("b1").WithLocation(28, 21), + // (29,16): error CS0103: The name 'b2' does not exist in the current context + // b2(Dummy(TakeOutParam(true, out var x8) && x8)); + Diagnostic(ErrorCode.ERR_NameNotInContext, "b2").WithArguments("b2").WithLocation(29, 16), + // (29,52): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // b2(Dummy(TakeOutParam(true, out var x8) && x8)); - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x8").WithArguments("x8").WithLocation(29, 52), - // (30,47): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(29, 52), + // (30,47): error CS0128: A local variable or function named 'x8' is already defined in this scope // Dummy(TakeOutParam(true, out var x8) && x8); - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(30, 47), - // (31,47): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + Diagnostic(ErrorCode.ERR_LocalDuplicate, "x8").WithArguments("x8").WithLocation(30, 47), + // (31,47): error CS0128: A local variable or function named 'x8' is already defined in this scope // Dummy(TakeOutParam(true, out var x8) && x8)) - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(31, 47), - // (37,23): error CS0841: Cannot use local variable 'x9' before it is declared + Diagnostic(ErrorCode.ERR_LocalDuplicate, "x8").WithArguments("x8").WithLocation(31, 47), + // (37,23): error CS0103: The name 'x9' does not exist in the current context // for (bool b = x9, - Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x9").WithArguments("x9").WithLocation(37, 23), + Diagnostic(ErrorCode.ERR_NameNotInContext, "x9").WithArguments("x9").WithLocation(37, 23), + // (38,16): error CS0103: The name 'b2' does not exist in the current context + // b2(Dummy(TakeOutParam(true, out var x9) && x9)); + Diagnostic(ErrorCode.ERR_NameNotInContext, "b2").WithArguments("b2").WithLocation(38, 16), // (39,47): error CS0136: A local or parameter named 'x9' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(TakeOutParam(true, out var x9) && x9); Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(39, 47), - // (40,47): error CS0136: A local or parameter named 'x9' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // (40,47): error CS0128: A local variable or function named 'x9' is already defined in this scope // Dummy(TakeOutParam(true, out var x9) && x9)) - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(40, 47) - ); + Diagnostic(ErrorCode.ERR_LocalDuplicate, "x9").WithArguments("x9").WithLocation(40, 47)); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree); var x4Decl = GetOutVarDeclarations(tree, "x4").Single(); - var x4Ref = GetReferences(tree, "x4").Single(); - AssertContainedInDeclaratorArguments(x4Decl); - VerifyModelForOutVarDuplicateInSameScope(model, x4Decl); - VerifyNotAnOutLocal(model, x4Ref); + var x4Ref = GetReferences(tree, "x4").ToArray(); + AssertNotContainedInDeclaratorArguments(x4Decl); + VerifyModelForOutVarWithoutDataFlow(model, x4Decl, x4Ref); var x7Decl = GetOutVarDeclarations(tree, "x7").Single(); var x7Ref = GetReferences(tree, "x7").Single(); - AssertContainedInDeclaratorArguments(x7Decl); - VerifyModelForOutVarDuplicateInSameScope(model, x7Decl); - VerifyNotAnOutLocal(model, x7Ref); + AssertNotContainedInDeclaratorArguments(x7Decl); + VerifyModelForOutVarWithoutDataFlow(model, x7Decl, x7Ref); var x8Decl = GetOutVarDeclarations(tree, "x8").ToArray(); var x8Ref = GetReferences(tree, "x8").ToArray(); Assert.Equal(4, x8Decl.Length); Assert.Equal(4, x8Ref.Length); - AssertContainedInDeclaratorArguments(x8Decl[0]); - AssertContainedInDeclaratorArguments(x8Decl[1]); - VerifyModelForOutVarWithoutDataFlow(model, x8Decl[0], x8Ref[0], x8Ref[1]); - VerifyModelForOutVarDuplicateInSameScope(model, x8Decl[1]); - VerifyModelForOutVar(model, x8Decl[2], x8Ref[2]); - VerifyModelForOutVar(model, x8Decl[3], x8Ref[3]); + AssertNotContainedInDeclaratorArguments(x8Decl[0]); + AssertNotContainedInDeclaratorArguments(x8Decl[1]); + VerifyModelForOutVar(model, x8Decl[0], x8Ref[0]); + VerifyModelForOutVar(model, x8Decl[1], x8Ref[1]); + VerifyModelForOutVarWithoutDataFlow(model, x8Decl[2], isShadowed: true); + VerifyModelForOutVarWithoutDataFlow(model, x8Decl[3], isShadowed: true); var x9Decl = GetOutVarDeclarations(tree, "x9").ToArray(); var x9Ref = GetReferences(tree, "x9").ToArray(); Assert.Equal(3, x9Decl.Length); Assert.Equal(4, x9Ref.Length); - AssertContainedInDeclaratorArguments(x9Decl[0]); - VerifyModelForOutVarWithoutDataFlow(model, x9Decl[0], x9Ref[0], x9Ref[1]); + AssertNotContainedInDeclaratorArguments(x9Decl[0]); + VerifyModelForOutVar(model, x9Decl[0], x9Ref[1]); VerifyModelForOutVar(model, x9Decl[1], x9Ref[2]); - VerifyModelForOutVar(model, x9Decl[2], x9Ref[3]); + VerifyModelForOutVarWithoutDataFlow(model, x9Decl[2], isShadowed: true); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTestBase.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTestBase.cs index 8d77f638ce690..d37a1bb88a5b7 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTestBase.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTestBase.cs @@ -350,6 +350,9 @@ protected static void AssertContainedInDeclaratorArguments(SingleVariableDesigna Assert.True(decl.Ancestors().OfType().First().ArgumentList.Contains(decl)); } + protected static void AssertNotContainedInDeclaratorArguments(SingleVariableDesignationSyntax decl) + => Assert.Empty(decl.Ancestors().OfType()); + protected static void AssertContainedInDeclaratorArguments(params SingleVariableDesignationSyntax[] decls) { foreach (var decl in decls) @@ -358,6 +361,12 @@ protected static void AssertContainedInDeclaratorArguments(params SingleVariable } } + protected static void AssertNotContainedInDeclaratorArguments(params SingleVariableDesignationSyntax[] decls) + { + foreach (var decl in decls) + AssertNotContainedInDeclaratorArguments(decl); + } + protected static void VerifyModelNotSupported( SemanticModel model, SingleVariableDesignationSyntax designation, diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests_Scope.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests_Scope.cs index 3f5a3e860c02c..cfb5fc42fed94 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests_Scope.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests_Scope.cs @@ -12394,48 +12394,82 @@ void Test14() } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular); - int[] exclude = new int[] { (int)ErrorCode.ERR_BadVarDecl, - (int)ErrorCode.ERR_SyntaxError, - (int)ErrorCode.ERR_UnexpectedToken, - (int)ErrorCode.WRN_UnreferencedVar, - (int)ErrorCode.ERR_UseDefViolation - }; + int[] exclude = [ + (int)ErrorCode.ERR_BadVarDecl, + (int)ErrorCode.ERR_SyntaxError, + (int)ErrorCode.ERR_UnexpectedToken, + (int)ErrorCode.WRN_UnreferencedVar, + (int)ErrorCode.ERR_UseDefViolation, + (int)ErrorCode.ERR_AbstractAndExtern, + (int)ErrorCode.ERR_SemicolonExpected, + (int)ErrorCode.ERR_CloseParenExpected, + (int)ErrorCode.ERR_InvalidExprTerm, + ]; compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( - // (109,13): error CS1023: Embedded statement cannot be a declaration or labeled statement - // var y12 = 12; - Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "var y12 = 12;").WithLocation(109, 13), + // (12,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(12, 22), + // (22,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(22, 22), + // (33,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(33, 22), // (34,32): error CS0136: A local or parameter named 'x4' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(true is var x4 && x4) Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x4").WithArguments("x4").WithLocation(34, 32), + // (41,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(41, 22), // (42,20): error CS0841: Cannot use local variable 'x6' before it is declared // Dummy(x6 && true is var x6) Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x6").WithArguments("x6").WithLocation(42, 20), + // (49,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(49, 22), // (53,17): error CS0136: A local or parameter named 'x7' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // var x7 = 12; Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x7").WithArguments("x7").WithLocation(53, 17), + // (60,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(60, 22), // (65,34): error CS0103: The name 'x8' does not exist in the current context // System.Console.WriteLine(x8); Diagnostic(ErrorCode.ERR_NameNotInContext, "x8").WithArguments("x8").WithLocation(65, 34), - // (65,9): warning CS0162: Unreachable code detected - // System.Console.WriteLine(x8); - Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(65, 9), + // (70,23): error CS0103: The name 'b1' does not exist in the current context + // for (bool a1, b1( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b1").WithArguments("b1").WithLocation(70, 23), + // (75,27): error CS0103: The name 'b2' does not exist in the current context + // for (bool a2, b2( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b2").WithArguments("b2").WithLocation(75, 27), // (76,36): error CS0136: A local or parameter named 'x9' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(true is var x9 && x9) // 2 Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(76, 36), + // (84,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(84, 22), // (85,20): error CS0103: The name 'y10' does not exist in the current context // Dummy(y10 is var x10) Diagnostic(ErrorCode.ERR_NameNotInContext, "y10").WithArguments("y10").WithLocation(85, 20), + // (106,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(106, 22), // (107,20): error CS0103: The name 'y12' does not exist in the current context // Dummy(y12 is var x12) Diagnostic(ErrorCode.ERR_NameNotInContext, "y12").WithArguments("y12").WithLocation(107, 20), + // (109,13): error CS1023: Embedded statement cannot be a declaration or labeled statement + // var y12 = 12; + Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "var y12 = 12;").WithLocation(109, 13), // (109,17): warning CS0219: The variable 'y12' is assigned but its value is never used // var y12 = 12; Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "y12").WithArguments("y12").WithLocation(109, 17), + // (122,22): error CS0103: The name 'b' does not exist in the current context + // for (bool a, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(122, 22), // (124,29): error CS0128: A local variable or function named 'x14' is already defined in this scope // 2 is var x14, - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x14").WithArguments("x14").WithLocation(124, 29) - ); + Diagnostic(ErrorCode.ERR_LocalDuplicate, "x14").WithArguments("x14").WithLocation(124, 29)); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree); @@ -12443,39 +12477,39 @@ void Test14() var x1Decl = GetPatternDeclarations(tree, "x1").Single(); var x1Ref = GetReferences(tree, "x1").ToArray(); Assert.Equal(2, x1Ref.Length); - AssertContainedInDeclaratorArguments(x1Decl); + AssertNotContainedInDeclaratorArguments(x1Decl); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x1Decl, x1Ref); var x2Decl = GetPatternDeclarations(tree, "x2").Single(); var x2Ref = GetReferences(tree, "x2").ToArray(); Assert.Equal(2, x2Ref.Length); - AssertContainedInDeclaratorArguments(x2Decl); + AssertNotContainedInDeclaratorArguments(x2Decl); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x2Decl, x2Ref); var x4Decl = GetPatternDeclarations(tree, "x4").Single(); var x4Ref = GetReferences(tree, "x4").ToArray(); Assert.Equal(3, x4Ref.Length); - AssertContainedInDeclaratorArguments(x4Decl); + AssertNotContainedInDeclaratorArguments(x4Decl); VerifyNotAPatternLocal(model, x4Ref[0]); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x4Decl, x4Ref[1], x4Ref[2]); var x6Decl = GetPatternDeclarations(tree, "x6").Single(); var x6Ref = GetReferences(tree, "x6").ToArray(); Assert.Equal(2, x6Ref.Length); - AssertContainedInDeclaratorArguments(x6Decl); + AssertNotContainedInDeclaratorArguments(x6Decl); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x6Decl, x6Ref); var x7Decl = GetPatternDeclarations(tree, "x7").Single(); var x7Ref = GetReferences(tree, "x7").ToArray(); Assert.Equal(2, x7Ref.Length); - AssertContainedInDeclaratorArguments(x7Decl); + AssertNotContainedInDeclaratorArguments(x7Decl); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x7Decl, x7Ref[0]); VerifyNotAPatternLocal(model, x7Ref[1]); var x8Decl = GetPatternDeclarations(tree, "x8").Single(); var x8Ref = GetReferences(tree, "x8").ToArray(); Assert.Equal(3, x8Ref.Length); - AssertContainedInDeclaratorArguments(x8Decl); + AssertNotContainedInDeclaratorArguments(x8Decl); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x8Decl, x8Ref[0], x8Ref[1]); VerifyNotInScope(model, x8Ref[2]); @@ -12483,7 +12517,7 @@ void Test14() var x9Ref = GetReferences(tree, "x9").ToArray(); Assert.Equal(2, x9Decl.Length); Assert.Equal(4, x9Ref.Length); - AssertContainedInDeclaratorArguments(x9Decl); + AssertNotContainedInDeclaratorArguments(x9Decl); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x9Decl[0], x9Ref[0], x9Ref[1]); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x9Decl[1], x9Ref[2], x9Ref[3]); @@ -12499,7 +12533,7 @@ void Test14() var x14Ref = GetReferences(tree, "x14").ToArray(); Assert.Equal(2, x14Decl.Length); Assert.Equal(2, x14Ref.Length); - AssertContainedInDeclaratorArguments(x14Decl); + AssertNotContainedInDeclaratorArguments(x14Decl); VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x14Decl[0], x14Ref); VerifyModelForDeclarationOrVarPatternDuplicateInSameScope(model, x14Decl[1]); } @@ -12553,77 +12587,92 @@ void Test9() } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular); - int[] exclude = new int[] { (int)ErrorCode.ERR_BadVarDecl, - (int)ErrorCode.ERR_SyntaxError, - (int)ErrorCode.ERR_UnexpectedToken, - (int)ErrorCode.WRN_UnreferencedVar, - (int)ErrorCode.ERR_CloseParenExpected - }; + int[] exclude = [ + (int)ErrorCode.ERR_BadVarDecl, + (int)ErrorCode.ERR_SyntaxError, + (int)ErrorCode.ERR_UnexpectedToken, + (int)ErrorCode.WRN_UnreferencedVar, + (int)ErrorCode.ERR_CloseParenExpected, + (int)ErrorCode.ERR_SemicolonExpected, + (int)ErrorCode.ERR_InvalidExprTerm, + ]; compilation.GetDiagnostics().Where(d => !exclude.Contains(d.Code)).Verify( - // (13,32): error CS0128: A local variable or function named 'x4' is already defined in this scope - // Dummy(true is var x4 && x4) - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x4").WithArguments("x4").WithLocation(13, 32), - // (21,32): error CS0128: A local variable or function named 'x7' is already defined in this scope + // (12,22): error CS0841: Cannot use local variable 'x4' before it is declared + // for (bool d, x4( + Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x4").WithArguments("x4").WithLocation(12, 22), + // (20,30): error CS0103: The name 'b' does not exist in the current context + // for (bool x7 = true, b( + Diagnostic(ErrorCode.ERR_NameNotInContext, "b").WithArguments("b").WithLocation(20, 30), + // (21,32): error CS0136: A local or parameter named 'x7' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(true is var x7 && x7) - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x7").WithArguments("x7").WithLocation(21, 32), + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x7").WithArguments("x7").WithLocation(21, 32), // (20,19): warning CS0219: The variable 'x7' is assigned but its value is never used // for (bool x7 = true, b( Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x7").WithArguments("x7").WithLocation(20, 19), - // (29,37): error CS0128: A local variable or function named 'x8' is already defined in this scope + // (28,21): error CS0103: The name 'b1' does not exist in the current context + // for (bool d,b1(Dummy(true is var x8 && x8)], + Diagnostic(ErrorCode.ERR_NameNotInContext, "b1").WithArguments("b1").WithLocation(28, 21), + // (29,16): error CS0103: The name 'b2' does not exist in the current context + // b2(Dummy(true is var x8 && x8)); + Diagnostic(ErrorCode.ERR_NameNotInContext, "b2").WithArguments("b2").WithLocation(29, 16), + // (29,37): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // b2(Dummy(true is var x8 && x8)); - Diagnostic(ErrorCode.ERR_LocalDuplicate, "x8").WithArguments("x8").WithLocation(29, 37), - // (30,32): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(29, 37), + // (30,32): error CS0128: A local variable or function named 'x8' is already defined in this scope // Dummy(true is var x8 && x8); - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(30, 32), - // (31,32): error CS0136: A local or parameter named 'x8' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + Diagnostic(ErrorCode.ERR_LocalDuplicate, "x8").WithArguments("x8").WithLocation(30, 32), + // (31,32): error CS0128: A local variable or function named 'x8' is already defined in this scope // Dummy(true is var x8 && x8)) - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x8").WithArguments("x8").WithLocation(31, 32), - // (37,23): error CS0841: Cannot use local variable 'x9' before it is declared + Diagnostic(ErrorCode.ERR_LocalDuplicate, "x8").WithArguments("x8").WithLocation(31, 32), + // (37,23): error CS0103: The name 'x9' does not exist in the current context // for (bool b = x9, - Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "x9").WithArguments("x9").WithLocation(37, 23), + Diagnostic(ErrorCode.ERR_NameNotInContext, "x9").WithArguments("x9").WithLocation(37, 23), + // (38,16): error CS0103: The name 'b2' does not exist in the current context + // b2(Dummy(true is var x9 && x9)); + Diagnostic(ErrorCode.ERR_NameNotInContext, "b2").WithArguments("b2").WithLocation(38, 16), // (39,32): error CS0136: A local or parameter named 'x9' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter // Dummy(true is var x9 && x9); Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(39, 32), - // (40,32): error CS0136: A local or parameter named 'x9' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // (40,32): error CS0128: A local variable or function named 'x9' is already defined in this scope // Dummy(true is var x9 && x9)) - Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "x9").WithArguments("x9").WithLocation(40, 32) - ); + Diagnostic(ErrorCode.ERR_LocalDuplicate, "x9").WithArguments("x9").WithLocation(40, 32)); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree); var x4Decl = GetPatternDeclarations(tree, "x4").Single(); - var x4Ref = GetReferences(tree, "x4").Single(); - AssertContainedInDeclaratorArguments(x4Decl); - VerifyModelForDeclarationOrVarPatternDuplicateInSameScope(model, x4Decl); - VerifyNotAPatternLocal(model, x4Ref); + var x4Ref = GetReferences(tree, "x4").ToArray(); + Assert.Equal(2, x4Ref.Length); + AssertNotContainedInDeclaratorArguments(x4Decl); + VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x4Decl, x4Ref[0]); + VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x4Decl, x4Ref[1]); var x7Decl = GetPatternDeclarations(tree, "x7").Single(); var x7Ref = GetReferences(tree, "x7").Single(); - AssertContainedInDeclaratorArguments(x7Decl); - VerifyModelForDeclarationOrVarPatternDuplicateInSameScope(model, x7Decl); - VerifyNotAPatternLocal(model, x7Ref); + AssertNotContainedInDeclaratorArguments(x7Decl); + VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x7Decl); + VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x7Decl, x7Ref); var x8Decl = GetPatternDeclarations(tree, "x8").ToArray(); var x8Ref = GetReferences(tree, "x8").ToArray(); Assert.Equal(4, x8Decl.Length); Assert.Equal(4, x8Ref.Length); - AssertContainedInDeclaratorArguments(x8Decl[0]); - AssertContainedInDeclaratorArguments(x8Decl[1]); - VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x8Decl[0], x8Ref[0], x8Ref[1]); - VerifyModelForDeclarationOrVarPatternDuplicateInSameScope(model, x8Decl[1]); - VerifyModelForDeclarationOrVarSimplePattern(model, x8Decl[2], x8Ref[2]); - VerifyModelForDeclarationOrVarSimplePattern(model, x8Decl[3], x8Ref[3]); + AssertNotContainedInDeclaratorArguments(x8Decl[0]); + AssertNotContainedInDeclaratorArguments(x8Decl[1]); + VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x8Decl[0], x8Ref[0]); + VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x8Decl[1], x8Ref[1]); + VerifyModelForDeclarationOrVarSimplePattern(model, x8Decl[2], isShadowed: true); + VerifyModelForDeclarationOrVarSimplePattern(model, x8Decl[3], isShadowed: true); var x9Decl = GetPatternDeclarations(tree, "x9").ToArray(); var x9Ref = GetReferences(tree, "x9").ToArray(); Assert.Equal(3, x9Decl.Length); Assert.Equal(4, x9Ref.Length); - AssertContainedInDeclaratorArguments(x9Decl[0]); - VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x9Decl[0], x9Ref[0], x9Ref[1]); + AssertNotContainedInDeclaratorArguments(x9Decl[0]); + VerifyModelForDeclarationOrVarSimplePatternWithoutDataFlow(model, x9Decl[0], x9Ref[1]); VerifyModelForDeclarationOrVarSimplePattern(model, x9Decl[1], x9Ref[2]); - VerifyModelForDeclarationOrVarSimplePattern(model, x9Decl[2], x9Ref[3]); + VerifyModelForDeclarationOrVarSimplePattern(model, x9Decl[2], isShadowed: true); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/PrimaryConstructorTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/PrimaryConstructorTests.cs index ef70e6a583910..3559b208e34e7 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/PrimaryConstructorTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/PrimaryConstructorTests.cs @@ -9540,7 +9540,10 @@ class C1 (int p1) comp1.VerifyEmitDiagnostics( // (4,38): error CS1041: Identifier expected; 'delegate' is a keyword // public System.Func M21() => delegate => p1; - Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "delegate").WithArguments("", "delegate").WithLocation(4, 38) + Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "delegate").WithArguments("", "delegate").WithLocation(4, 38), + // (4,47): error CS1593: Delegate 'Func' does not take 1 arguments + // public System.Func M21() => delegate => p1; + Diagnostic(ErrorCode.ERR_BadDelArgCount, "=>").WithArguments("System.Func", "1").WithLocation(4, 47) ); var source = @" diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IConditionalAccessExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IConditionalAccessExpression.cs index b88f5668d9c04..7d9c663c2218e 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IConditionalAccessExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IConditionalAccessExpression.cs @@ -993,7 +993,6 @@ public struct S1 Statements (0) Next (Regular) Block[B1] Entering: {R1} {R2} - .locals {R1} { CaptureIds: [0] @@ -1004,61 +1003,58 @@ public struct S1 Predecessors: [B0] Statements (1) IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'x') - Value: + Value: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: S1?, IsInvalid) (Syntax: 'x') - Jump if True (Regular) to Block[B3] IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsInvalid, IsImplicit) (Syntax: 'x') - Operand: + Operand: IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: S1?, IsInvalid, IsImplicit) (Syntax: 'x') Leaving: {R2} - Next (Regular) Block[B2] Block[B2] - Block Predecessors: [B1] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: '.P1') - Value: + Value: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: '.P1') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (ImplicitNullable) - Operand: + Operand: IPropertyReferenceOperation: System.Int32 S1.P1 { get; set; } (OperationKind.PropertyReference, Type: System.Int32, IsInvalid) (Syntax: '.P1') - Instance Receiver: + Instance Receiver: IInvocationOperation ( S1 S1?.GetValueOrDefault()) (OperationKind.Invocation, Type: S1, IsInvalid, IsImplicit) (Syntax: 'x') - Instance Receiver: + Instance Receiver: IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: S1?, IsInvalid, IsImplicit) (Syntax: 'x') Arguments(0) - Next (Regular) Block[B4] Leaving: {R2} } - Block[B3] - Block Predecessors: [B1] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'x') - Value: + Value: IDefaultValueOperation (OperationKind.DefaultValue, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x') - Next (Regular) Block[B4] Block[B4] - Block Predecessors: [B2] [B3] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: 'x?.P1 = 0;') - Expression: + Expression: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32?, IsInvalid) (Syntax: 'x?.P1 = 0') - Left: + Left: IInvalidOperation (OperationKind.Invalid, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x?.P1') Children(1): IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x?.P1') - Right: - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') - + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32?, IsImplicit) (Syntax: '0') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (ImplicitNullable) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') Next (Regular) Block[B5] Leaving: {R1} } - Block[B5] - Exit Predecessors: [B4] Statements (0) @@ -1094,7 +1090,6 @@ public struct S1 Statements (0) Next (Regular) Block[B1] Entering: {R1} {R2} - .locals {R1} { CaptureIds: [0] @@ -1105,61 +1100,58 @@ public struct S1 Predecessors: [B0] Statements (1) IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'x') - Value: + Value: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: S1?, IsInvalid) (Syntax: 'x') - Jump if True (Regular) to Block[B3] IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsInvalid, IsImplicit) (Syntax: 'x') - Operand: + Operand: IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: S1?, IsInvalid, IsImplicit) (Syntax: 'x') Leaving: {R2} - Next (Regular) Block[B2] Block[B2] - Block Predecessors: [B1] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: '.P1') - Value: + Value: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: '.P1') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (ImplicitNullable) - Operand: + Operand: IFieldReferenceOperation: System.Int32 S1.P1 (OperationKind.FieldReference, Type: System.Int32, IsInvalid) (Syntax: '.P1') - Instance Receiver: + Instance Receiver: IInvocationOperation ( S1 S1?.GetValueOrDefault()) (OperationKind.Invocation, Type: S1, IsInvalid, IsImplicit) (Syntax: 'x') - Instance Receiver: + Instance Receiver: IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: S1?, IsInvalid, IsImplicit) (Syntax: 'x') Arguments(0) - Next (Regular) Block[B4] Leaving: {R2} } - Block[B3] - Block Predecessors: [B1] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'x') - Value: + Value: IDefaultValueOperation (OperationKind.DefaultValue, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x') - Next (Regular) Block[B4] Block[B4] - Block Predecessors: [B2] [B3] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: 'x?.P1 = 0;') - Expression: + Expression: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32?, IsInvalid) (Syntax: 'x?.P1 = 0') - Left: + Left: IInvalidOperation (OperationKind.Invalid, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x?.P1') Children(1): IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x?.P1') - Right: - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') - + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32?, IsImplicit) (Syntax: '0') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (ImplicitNullable) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') Next (Regular) Block[B5] Leaving: {R1} } - Block[B5] - Exit Predecessors: [B4] Statements (0) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IEventAssignmentExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IEventAssignmentExpression.cs index 99d436592c653..285df63cf73af 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IEventAssignmentExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IEventAssignmentExpression.cs @@ -967,21 +967,24 @@ void M(C c, EventHandler handler) Predecessors: [B0] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: '(c.MyEvent ... ndler) = 0;') - Expression: + Expression: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Void, IsInvalid) (Syntax: '(c.MyEvent ... andler) = 0') - Left: + Left: IInvalidOperation (OperationKind.Invalid, Type: System.Void, IsInvalid, IsImplicit) (Syntax: 'c.MyEvent += handler') Children(1): IEventAssignmentOperation (EventAdd) (OperationKind.EventAssignment, Type: System.Void, IsInvalid) (Syntax: 'c.MyEvent += handler') - Event Reference: + Event Reference: IEventReferenceOperation: event System.EventHandler C.MyEvent (OperationKind.EventReference, Type: System.EventHandler, IsInvalid) (Syntax: 'c.MyEvent') - Instance Receiver: + Instance Receiver: IParameterReferenceOperation: c (OperationKind.ParameterReference, Type: C, IsInvalid) (Syntax: 'c') - Handler: + Handler: IParameterReferenceOperation: handler (OperationKind.ParameterReference, Type: System.EventHandler, IsInvalid) (Syntax: 'handler') - Right: - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') - + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Void, IsImplicit) (Syntax: '0') + Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (NoConversion) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') Next (Regular) Block[B2] Block[B2] - Exit Predecessors: [B1] @@ -1019,7 +1022,6 @@ void M(C c, EventHandler handler, int? x1, int x2) Statements (0) Next (Regular) Block[B1] Entering: {R1} - .locals {R1} { CaptureIds: [0] [2] @@ -1027,20 +1029,18 @@ void M(C c, EventHandler handler, int? x1, int x2) Predecessors: [B0] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'c.MyEvent += handler') - Value: + Value: IInvalidOperation (OperationKind.Invalid, Type: System.Void, IsInvalid, IsImplicit) (Syntax: 'c.MyEvent += handler') Children(1): IEventAssignmentOperation (EventAdd) (OperationKind.EventAssignment, Type: System.Void, IsInvalid) (Syntax: 'c.MyEvent += handler') - Event Reference: + Event Reference: IEventReferenceOperation: event System.EventHandler C.MyEvent (OperationKind.EventReference, Type: System.EventHandler, IsInvalid) (Syntax: 'c.MyEvent') - Instance Receiver: + Instance Receiver: IParameterReferenceOperation: c (OperationKind.ParameterReference, Type: C, IsInvalid) (Syntax: 'c') - Handler: + Handler: IParameterReferenceOperation: handler (OperationKind.ParameterReference, Type: System.EventHandler, IsInvalid) (Syntax: 'handler') - Next (Regular) Block[B2] Entering: {R2} - .locals {R2} { CaptureIds: [1] @@ -1048,53 +1048,50 @@ void M(C c, EventHandler handler, int? x1, int x2) Predecessors: [B1] Statements (1) IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'x1') - Value: + Value: IParameterReferenceOperation: x1 (OperationKind.ParameterReference, Type: System.Int32?) (Syntax: 'x1') - Jump if True (Regular) to Block[B4] IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'x1') - Operand: + Operand: IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32?, IsImplicit) (Syntax: 'x1') Leaving: {R2} - Next (Regular) Block[B3] Block[B3] - Block Predecessors: [B2] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'x1') - Value: + Value: IInvocationOperation ( System.Int32 System.Int32?.GetValueOrDefault()) (OperationKind.Invocation, Type: System.Int32, IsImplicit) (Syntax: 'x1') - Instance Receiver: + Instance Receiver: IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32?, IsImplicit) (Syntax: 'x1') Arguments(0) - Next (Regular) Block[B5] Leaving: {R2} } - Block[B4] - Block Predecessors: [B2] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'x2') - Value: + Value: IParameterReferenceOperation: x2 (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x2') - Next (Regular) Block[B5] Block[B5] - Block Predecessors: [B3] [B4] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: '(c.MyEvent ... = x1 ?? x2;') - Expression: + Expression: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Void, IsInvalid) (Syntax: '(c.MyEvent ... = x1 ?? x2') - Left: + Left: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Void, IsInvalid, IsImplicit) (Syntax: 'c.MyEvent += handler') - Right: - IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'x1 ?? x2') - + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Void, IsImplicit) (Syntax: 'x1 ?? x2') + Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (NoConversion) + Operand: + IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'x1 ?? x2') Next (Regular) Block[B6] Leaving: {R1} } - Block[B6] - Exit Predecessors: [B5] Statements (0) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForLoopStatement.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForLoopStatement.cs index b6177d5b04494..f0b3121c917de 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForLoopStatement.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForLoopStatement.cs @@ -2678,7 +2678,7 @@ static void Main(string[] args) [CompilerTrait(CompilerFeature.IOperation)] [Fact, WorkItem(17602, "https://github.com/dotnet/roslyn/issues/17602")] - public void IForLoopStatement_InvalidExpression() + public void IForLoopStatement_InvalidExpression1() { string source = @" class C @@ -2691,45 +2691,65 @@ static void Main(string[] args) } } "; - string expectedOperationTree = @" -IForLoopOperation (LoopKind.For, Continue Label Id: 0, Exit Label Id: 1) (OperationKind.Loop, Type: null, IsInvalid) (Syntax: 'for (int k ... 100, j > 5;') - Locals: Local_1: System.Int32 k - Local_2: System.Int32 j - Condition: - IBinaryOperation (BinaryOperatorKind.LessThan) (OperationKind.Binary, Type: System.Boolean, IsInvalid) (Syntax: 'k < 100') - Left: - ILocalReferenceOperation: k (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'k') - Right: - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 100, IsInvalid) (Syntax: '100') - Before: - IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsImplicit) (Syntax: 'int k = 0, j = 0') - IVariableDeclarationOperation (2 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'int k = 0, j = 0') - Declarators: - IVariableDeclaratorOperation (Symbol: System.Int32 k) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'k = 0') - Initializer: - IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= 0') - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') - IVariableDeclaratorOperation (Symbol: System.Int32 j) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'j = 0') - Initializer: - IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= 0') - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') - Initializer: - null - AtLoopBottom: - IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid, IsImplicit) (Syntax: '') - Expression: - IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: '') - Children(0) - IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid, IsImplicit) (Syntax: 'j > 5') - Expression: - IBinaryOperation (BinaryOperatorKind.GreaterThan) (OperationKind.Binary, Type: System.Boolean, IsInvalid) (Syntax: 'j > 5') - Left: - ILocalReferenceOperation: j (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'j') - Right: - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5, IsInvalid) (Syntax: '5') - Body: - IEmptyOperation (OperationKind.Empty, Type: null, IsInvalid) (Syntax: ';') + var tree = GetOperationTreeForTest(source); + Assert.Null(tree); + } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17602")] + public void IForLoopStatement_InvalidExpression2() + { + string source = @" +class C +{ + static void Main(string[] args) + { + /**/for (int k = 0, j = 0; k < 100, j > 5; k++) + { + }/**/ + } +} "; + string expectedOperationTree = """ + IForLoopOperation (LoopKind.For, Continue Label Id: 0, Exit Label Id: 1) (OperationKind.Loop, Type: null, IsInvalid) (Syntax: 'for (int k ... }') + Locals: Local_1: System.Int32 k + Local_2: System.Int32 j + Condition: + IBinaryOperation (BinaryOperatorKind.LessThan) (OperationKind.Binary, Type: System.Boolean, IsInvalid) (Syntax: 'k < 100') + Left: + ILocalReferenceOperation: k (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'k') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 100, IsInvalid) (Syntax: '100') + Before: + IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsImplicit) (Syntax: 'int k = 0, j = 0') + IVariableDeclarationOperation (2 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'int k = 0, j = 0') + Declarators: + IVariableDeclaratorOperation (Symbol: System.Int32 k) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'k = 0') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + IVariableDeclaratorOperation (Symbol: System.Int32 j) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'j = 0') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Initializer: + null + AtLoopBottom: + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid, IsImplicit) (Syntax: 'j > 5') + Expression: + IBinaryOperation (BinaryOperatorKind.GreaterThan) (OperationKind.Binary, Type: System.Boolean, IsInvalid) (Syntax: 'j > 5') + Left: + ILocalReferenceOperation: j (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'j') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5, IsInvalid) (Syntax: '5') + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: 'k++') + Expression: + IIncrementOrDecrementOperation (Postfix) (OperationKind.Increment, Type: System.Int32) (Syntax: 'k++') + Target: + ILocalReferenceOperation: k (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'k') + Body: + IBlockOperation (0 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }') + """; VerifyOperationTreeForTest(source, expectedOperationTree); } diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IObjectCreationExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IObjectCreationExpression.cs index a382338ea9d86..2ad39202f3db0 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IObjectCreationExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IObjectCreationExpression.cs @@ -9072,22 +9072,22 @@ public void M(bool b, object o) Predecessors: [B0] Statements (2) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'new C1 { P1 ... 2) = null }') - Value: + Value: IObjectCreationOperation (Constructor: C1..ctor()) (OperationKind.ObjectCreation, Type: C1, IsInvalid) (Syntax: 'new C1 { P1 ... 2) = null }') Arguments(0) - Initializer: + Initializer: null ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Object) (Syntax: 'P1 = null') - Left: + Left: IPropertyReferenceOperation: System.Object C1.P1 { get; set; } (OperationKind.PropertyReference, Type: System.Object) (Syntax: 'P1') - Instance Receiver: + Instance Receiver: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C1, IsInvalid, IsImplicit) (Syntax: 'new C1 { P1 ... 2) = null }') - Right: + Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, Constant: null, IsImplicit) (Syntax: 'null') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) (ImplicitReference) - Operand: + Operand: ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') Next (Regular) Block[B2] @@ -9103,14 +9103,14 @@ public void M(bool b, object o) Predecessors: [B1] Statements (1) IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'P1') - Value: + Value: IPropertyReferenceOperation: System.Object C1.P1 { get; set; } (OperationKind.PropertyReference, Type: System.Object, IsInvalid) (Syntax: 'P1') - Instance Receiver: + Instance Receiver: IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C1, IsInvalid, IsImplicit) (Syntax: 'P1') Jump if True (Regular) to Block[B4] IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsInvalid, IsImplicit) (Syntax: 'P1') - Operand: + Operand: IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Object, IsInvalid, IsImplicit) (Syntax: 'P1') Leaving: {R3} @@ -9119,7 +9119,7 @@ public void M(bool b, object o) Predecessors: [B2] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'P1') - Value: + Value: IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Object, IsInvalid, IsImplicit) (Syntax: 'P1') Next (Regular) Block[B5] @@ -9130,9 +9130,9 @@ public void M(bool b, object o) Predecessors: [B2] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'P2') - Value: + Value: IPropertyReferenceOperation: System.Object C1.P2 { get; set; } (OperationKind.PropertyReference, Type: System.Object, IsInvalid) (Syntax: 'P2') - Instance Receiver: + Instance Receiver: IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C1, IsInvalid, IsImplicit) (Syntax: 'P2') Next (Regular) Block[B5] @@ -9140,12 +9140,16 @@ public void M(bool b, object o) Predecessors: [B3] [B4] Statements (1) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Object, IsInvalid) (Syntax: '(P1 ?? P2) = null') - Left: + Left: IInvalidOperation (OperationKind.Invalid, Type: System.Object, IsInvalid, IsImplicit) (Syntax: 'P1 ?? P2') Children(1): IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: System.Object, IsInvalid, IsImplicit) (Syntax: 'P1 ?? P2') - Right: - ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, Constant: null, IsInvalid, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + (ImplicitReference) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') Next (Regular) Block[B6] Leaving: {R2} @@ -9155,9 +9159,9 @@ public void M(bool b, object o) Predecessors: [B5] Statements (1) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: C1, IsInvalid, IsImplicit) (Syntax: 'x = new C1 ... 2) = null }') - Left: + Left: ILocalReferenceOperation: x (IsDeclaration: True) (OperationKind.LocalReference, Type: C1, IsInvalid, IsImplicit) (Syntax: 'x = new C1 ... 2) = null }') - Right: + Right: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C1, IsInvalid, IsImplicit) (Syntax: 'new C1 { P1 ... 2) = null }') Next (Regular) Block[B7] @@ -9215,22 +9219,22 @@ public void M(bool b, object o) Predecessors: [B0] Statements (2) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'new C1 { P1 ... 2) = null }') - Value: + Value: IObjectCreationOperation (Constructor: C1..ctor()) (OperationKind.ObjectCreation, Type: C1, IsInvalid) (Syntax: 'new C1 { P1 ... 2) = null }') Arguments(0) - Initializer: + Initializer: null ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Object) (Syntax: 'P1 = null') - Left: + Left: IPropertyReferenceOperation: System.Object C1.P1 { get; set; } (OperationKind.PropertyReference, Type: System.Object) (Syntax: 'P1') - Instance Receiver: + Instance Receiver: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C1, IsInvalid, IsImplicit) (Syntax: 'new C1 { P1 ... 2) = null }') - Right: + Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, Constant: null, IsImplicit) (Syntax: 'null') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) (ImplicitReference) - Operand: + Operand: ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') Next (Regular) Block[B2] @@ -9246,14 +9250,14 @@ public void M(bool b, object o) Predecessors: [B1] Statements (1) IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'P1') - Value: + Value: IPropertyReferenceOperation: System.Object C1.P1 { get; set; } (OperationKind.PropertyReference, Type: System.Object, IsInvalid) (Syntax: 'P1') - Instance Receiver: + Instance Receiver: IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C1, IsInvalid, IsImplicit) (Syntax: 'P1') Jump if True (Regular) to Block[B4] IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsInvalid, IsImplicit) (Syntax: 'P1') - Operand: + Operand: IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Object, IsInvalid, IsImplicit) (Syntax: 'P1') Leaving: {R3} @@ -9262,7 +9266,7 @@ public void M(bool b, object o) Predecessors: [B2] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'P1') - Value: + Value: IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Object, IsInvalid, IsImplicit) (Syntax: 'P1') Next (Regular) Block[B5] @@ -9273,9 +9277,9 @@ public void M(bool b, object o) Predecessors: [B2] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'P2') - Value: + Value: IFieldReferenceOperation: System.Object C1.P2 (OperationKind.FieldReference, Type: System.Object, IsInvalid) (Syntax: 'P2') - Instance Receiver: + Instance Receiver: IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: C1, IsInvalid, IsImplicit) (Syntax: 'P2') Next (Regular) Block[B5] @@ -9283,12 +9287,16 @@ public void M(bool b, object o) Predecessors: [B3] [B4] Statements (1) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Object, IsInvalid) (Syntax: '(P1 ?? P2) = null') - Left: + Left: IInvalidOperation (OperationKind.Invalid, Type: System.Object, IsInvalid, IsImplicit) (Syntax: 'P1 ?? P2') Children(1): IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: System.Object, IsInvalid, IsImplicit) (Syntax: 'P1 ?? P2') - Right: - ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, Constant: null, IsInvalid, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + (ImplicitReference) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') Next (Regular) Block[B6] Leaving: {R2} @@ -9298,9 +9306,9 @@ public void M(bool b, object o) Predecessors: [B5] Statements (1) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: C1, IsInvalid, IsImplicit) (Syntax: 'x = new C1 ... 2) = null }') - Left: + Left: ILocalReferenceOperation: x (IsDeclaration: True) (OperationKind.LocalReference, Type: C1, IsInvalid, IsImplicit) (Syntax: 'x = new C1 ... 2) = null }') - Right: + Right: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C1, IsInvalid, IsImplicit) (Syntax: 'new C1 { P1 ... 2) = null }') Next (Regular) Block[B7] diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ITupleExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ITupleExpression.cs index c86861a7cb158..27f239c24f185 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ITupleExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ITupleExpression.cs @@ -1885,11 +1885,11 @@ void M(bool b, int i1, int i2, int i3, int i4, int i5, int i6) Predecessors: [B1] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: '(i1, i2)') - Value: + Value: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Int32, System.Int32), IsInvalid, IsImplicit) (Syntax: '(i1, i2)') Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) - Operand: + Operand: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 i1, System.Int32 i2), IsInvalid) (Syntax: '(i1, i2)') NaturalType: (System.Int32 i1, System.Int32 i2) Elements(2): @@ -1901,11 +1901,11 @@ void M(bool b, int i1, int i2, int i3, int i4, int i5, int i6) Predecessors: [B1] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: '(i3, i4)') - Value: + Value: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Int32, System.Int32), IsInvalid, IsImplicit) (Syntax: '(i3, i4)') Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) - Operand: + Operand: ITupleOperation (OperationKind.Tuple, Type: (System.Int32 i3, System.Int32 i4), IsInvalid) (Syntax: '(i3, i4)') NaturalType: (System.Int32 i3, System.Int32 i4) Elements(2): @@ -1917,18 +1917,22 @@ void M(bool b, int i1, int i2, int i3, int i4, int i5, int i6) Predecessors: [B2] [B3] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: '(b ? (i1, i ... = (i5, i6);') - Expression: + Expression: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: (System.Int32, System.Int32), IsInvalid) (Syntax: '(b ? (i1, i ... = (i5, i6)') - Left: + Left: IInvalidOperation (OperationKind.Invalid, Type: (System.Int32, System.Int32), IsInvalid, IsImplicit) (Syntax: 'b ? (i1, i2) : (i3, i4)') Children(1): IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: (System.Int32, System.Int32), IsInvalid, IsImplicit) (Syntax: 'b ? (i1, i2) : (i3, i4)') - Right: - ITupleOperation (OperationKind.Tuple, Type: (System.Int32 i5, System.Int32 i6)) (Syntax: '(i5, i6)') - NaturalType: (System.Int32 i5, System.Int32 i6) - Elements(2): - IParameterReferenceOperation: i5 (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'i5') - IParameterReferenceOperation: i6 (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'i6') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (System.Int32, System.Int32), IsImplicit) (Syntax: '(i5, i6)') + Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + Operand: + ITupleOperation (OperationKind.Tuple, Type: (System.Int32 i5, System.Int32 i6)) (Syntax: '(i5, i6)') + NaturalType: (System.Int32 i5, System.Int32 i6) + Elements(2): + IParameterReferenceOperation: i5 (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'i5') + IParameterReferenceOperation: i6 (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'i6') Next (Regular) Block[B5] Leaving: {R1} @@ -1994,11 +1998,11 @@ void M(bool b) Predecessors: [B1] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: '(var i1, var i2)') - Value: + Value: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (var, var), IsInvalid, IsImplicit) (Syntax: '(var i1, var i2)') Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) - Operand: + Operand: ITupleOperation (OperationKind.Tuple, Type: (var i1, var i2), IsInvalid) (Syntax: '(var i1, var i2)') NaturalType: (var i1, var i2) Elements(2): @@ -2012,11 +2016,11 @@ void M(bool b) Predecessors: [B1] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: '(var i3, var i4)') - Value: + Value: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (var, var), IsInvalid, IsImplicit) (Syntax: '(var i3, var i4)') Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) - Operand: + Operand: ITupleOperation (OperationKind.Tuple, Type: (var i3, var i4), IsInvalid) (Syntax: '(var i3, var i4)') NaturalType: (var i3, var i4) Elements(2): @@ -2030,18 +2034,22 @@ void M(bool b) Predecessors: [B2] [B3] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: '(b ? (var i ... ) = (1, 2);') - Expression: + Expression: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: (var, var), IsInvalid) (Syntax: '(b ? (var i ... )) = (1, 2)') - Left: + Left: IInvalidOperation (OperationKind.Invalid, Type: (var, var), IsInvalid, IsImplicit) (Syntax: 'b ? (var i1 ... i3, var i4)') Children(1): IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: (var, var), IsInvalid, IsImplicit) (Syntax: 'b ? (var i1 ... i3, var i4)') - Right: - ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(1, 2)') - NaturalType: (System.Int32, System.Int32) - Elements(2): - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: (var, var), IsImplicit) (Syntax: '(1, 2)') + Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (NoConversion) + Operand: + ITupleOperation (OperationKind.Tuple, Type: (System.Int32, System.Int32)) (Syntax: '(1, 2)') + NaturalType: (System.Int32, System.Int32) + Elements(2): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') Next (Regular) Block[B5] Leaving: {R1} diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ForLoopErrorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ForLoopErrorTests.cs index 19146c1c9979a..43bf0dfdc598b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ForLoopErrorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ForLoopErrorTests.cs @@ -2,9 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - -using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics @@ -27,16 +24,16 @@ static void Main(string[] args) } } "; - CreateCompilation(text). - VerifyDiagnostics( - Diagnostic(ErrorCode.ERR_SemicolonExpected, ","), - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(","), - Diagnostic(ErrorCode.ERR_CloseParenExpected, ";"), - Diagnostic(ErrorCode.ERR_SemicolonExpected, ")"), - Diagnostic(ErrorCode.ERR_RbraceExpected, ")"), - Diagnostic(ErrorCode.ERR_IllegalStatement, "j > 5"), - Diagnostic(ErrorCode.ERR_NameNotInContext, "k").WithArguments("k") - ); + CreateCompilation(text).VerifyDiagnostics( + // (6,39): error CS1002: ; expected + // for (int k = 0, j = 0; k < 100, j > 5; k++) + Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(6, 39), + // (6,46): error CS1003: Syntax error, ',' expected + // for (int k = 0, j = 0; k < 100, j > 5; k++) + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(6, 46), + // (6,41): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // for (int k = 0, j = 0; k < 100, j > 5; k++) + Diagnostic(ErrorCode.ERR_IllegalStatement, "j > 5").WithLocation(6, 41)); } // Condition expression must be bool type @@ -94,11 +91,16 @@ static void Main(string[] args) } } "; - CreateCompilation(text). - VerifyDiagnostics( - Diagnostic(ErrorCode.ERR_CloseParenExpected, ";"), - Diagnostic(ErrorCode.ERR_RbraceExpected, ")") - ); + CreateCompilation(text).VerifyDiagnostics( + // (6,34): error CS1525: Invalid expression term ';' + // for (int i = 10; i < 100;;); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(6, 34), + // (6,34): error CS1003: Syntax error, ',' expected + // for (int i = 10; i < 100;;); + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(6, 34), + // (6,35): error CS1525: Invalid expression term ')' + // for (int i = 10; i < 100;;); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(6, 35)); text = @" diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs index aee69356d07eb..f6e980d11d3e9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs @@ -4152,5 +4152,54 @@ public unsafe void M(delegate*? f) { Diagnostic(ErrorCode.ERR_BadTypeArgument, "f").WithArguments("delegate*").WithLocation(5, 43) ); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75933")] + public void BoolLiteralInvocation_01() + { + var source = """ + #nullable enable + + class C + { + static unsafe void M() + { + int i = 0; + ((delegate*)true)(i); + } + } + """; + + var comp = CreateCompilationWithFunctionPointers(source); + comp.VerifyEmitDiagnostics( + // (8,10): error CS0030: Cannot convert type 'bool' to 'delegate*' + // ((delegate*)true)(i); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(delegate*)true").WithArguments("bool", "delegate*").WithLocation(8, 10) + ); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75933")] + public void BoolLiteralInvocation_02() + { + var source = """ + #nullable enable + + using System; + + class C + { + static unsafe void M() + { + var d = delegate (object? o, IntPtr f) { return ((delegate* managed)false)(o); }; + } + } + """; + + var comp = CreateCompilationWithFunctionPointers(source); + comp.VerifyEmitDiagnostics( + // (9,58): error CS0030: Cannot convert type 'bool' to 'delegate*' + // var d = delegate (object? o, IntPtr f) { return ((delegate* managed)false)(o); }; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(delegate* managed)false").WithArguments("bool", "delegate*").WithLocation(9, 58) + ); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/GenericConstraintsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/GenericConstraintsTests.cs index b80cafc4cad8f..cac8e9be0826b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/GenericConstraintsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/GenericConstraintsTests.cs @@ -3662,7 +3662,7 @@ public struct YourStruct where T : unmanaged } [Fact] - public void UnmanagedExpandingTypeArgumentConstraintViolation() + public void UnmanagedExpandingTypeArgumentConstraintViolation_01() { var code = @" public struct MyStruct @@ -3689,6 +3689,161 @@ public struct YourStruct where T : unmanaged Assert.False(compilation.GetMember("YourStruct").IsManagedTypeNoUseSiteDiagnostics); } + [Fact] + public void UnmanagedExpandingTypeArgumentConstraintViolation_02() + { + var code = @" +public struct MyStruct +{ + public string s; + public YourStruct>> field; +} + +public struct YourStruct where T : unmanaged +{ + public T field; +} +"; + var compilation = CreateCompilation(code, options: TestOptions.UnsafeReleaseDll); + compilation.VerifyDiagnostics( + // (5,46): error CS0523: Struct member 'MyStruct.field' of type 'YourStruct>>' causes a cycle in the struct layout + // public YourStruct>> field; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "field").WithArguments("MyStruct.field", "YourStruct>>").WithLocation(5, 46), + // (5,46): error CS8377: The type 'MyStruct>' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'YourStruct' + // public YourStruct>> field; + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "field").WithArguments("YourStruct", "T", "MyStruct>").WithLocation(5, 46)); + + Assert.True(compilation.GetMember("MyStruct").IsManagedTypeNoUseSiteDiagnostics); + Assert.False(compilation.GetMember("YourStruct").IsManagedTypeNoUseSiteDiagnostics); + } + + [Fact] + public void UnmanagedExpandingTypeArgumentConstraintViolation_03() + { + var code = @" +public struct MyStruct +{ + public YourStruct>> field {get;set;} + public string s; +} + +public struct YourStruct where T : unmanaged +{ + public T field; +} +"; + var compilation = CreateCompilation(code, options: TestOptions.UnsafeReleaseDll); + compilation.VerifyDiagnostics( + // (4,46): error CS0523: Struct member 'MyStruct.field' of type 'YourStruct>>' causes a cycle in the struct layout + // public YourStruct>> field; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "field").WithArguments("MyStruct.field", "YourStruct>>").WithLocation(4, 46), + // (4,46): error CS8377: The type 'MyStruct>' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'YourStruct' + // public YourStruct>> field; + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "field").WithArguments("YourStruct", "T", "MyStruct>").WithLocation(4, 46)); + + Assert.True(compilation.GetMember("MyStruct").IsManagedTypeNoUseSiteDiagnostics); + Assert.False(compilation.GetMember("YourStruct").IsManagedTypeNoUseSiteDiagnostics); + } + + [Fact] + public void UnmanagedExpandingTypeArgumentConstraintViolation_04() + { + var code = @" +public struct MyStruct +{ + public string s; + public YourStruct>> field {get;set;} +} + +public struct YourStruct where T : unmanaged +{ + public T field; +} +"; + var compilation = CreateCompilation(code, options: TestOptions.UnsafeReleaseDll); + compilation.VerifyDiagnostics( + // (5,46): error CS0523: Struct member 'MyStruct.field' of type 'YourStruct>>' causes a cycle in the struct layout + // public YourStruct>> field; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "field").WithArguments("MyStruct.field", "YourStruct>>").WithLocation(5, 46), + // (5,46): error CS8377: The type 'MyStruct>' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'YourStruct' + // public YourStruct>> field; + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "field").WithArguments("YourStruct", "T", "MyStruct>").WithLocation(5, 46)); + + Assert.True(compilation.GetMember("MyStruct").IsManagedTypeNoUseSiteDiagnostics); + Assert.False(compilation.GetMember("YourStruct").IsManagedTypeNoUseSiteDiagnostics); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/75620")] + public void UnmanagedExpandingTypeArgumentConstraintViolation_05() + { + var code = @" +#pragma warning disable CS0067 // The event 'MyStruct.field' is never used + +public struct MyStruct +{ + public event YourStruct>> field; + public string s; +} + +public struct YourStruct where T : unmanaged +{ + public T field; +} +"; + var compilation = CreateCompilation(code, options: TestOptions.UnsafeReleaseDll); + + compilation.VerifyDiagnostics( + // (6,52): error CS0523: Struct member 'MyStruct.field' of type 'YourStruct>>' causes a cycle in the struct layout + // public event YourStruct>> field; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "field").WithArguments("MyStruct.field", "YourStruct>>").WithLocation(6, 52), + // (6,52): error CS0066: 'MyStruct.field': event must be of a delegate type + // public event YourStruct>> field; + Diagnostic(ErrorCode.ERR_EventNotDelegate, "field").WithArguments("MyStruct.field").WithLocation(6, 52), + // (6,52): error CS8377: The type 'MyStruct>' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'YourStruct' + // public event YourStruct>> field; + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "field").WithArguments("YourStruct", "T", "MyStruct>").WithLocation(6, 52) + ); + + Assert.True(compilation.GetMember("MyStruct").IsManagedTypeNoUseSiteDiagnostics); + Assert.False(compilation.GetMember("YourStruct").IsManagedTypeNoUseSiteDiagnostics); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/75620")] + public void UnmanagedExpandingTypeArgumentConstraintViolation_06() + { + var code = @" +#pragma warning disable CS0067 // The event 'MyStruct.field' is never used + +public struct MyStruct +{ + public string s; + public event YourStruct>> field; +} + +public struct YourStruct where T : unmanaged +{ + public T field; +} +"; + var compilation = CreateCompilation(code, options: TestOptions.UnsafeReleaseDll); + compilation.VerifyDiagnostics( + // (7,52): error CS0523: Struct member 'MyStruct.field' of type 'YourStruct>>' causes a cycle in the struct layout + // public event YourStruct>> field; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "field").WithArguments("MyStruct.field", "YourStruct>>").WithLocation(7, 52), + // (7,52): error CS0066: 'MyStruct.field': event must be of a delegate type + // public event YourStruct>> field; + Diagnostic(ErrorCode.ERR_EventNotDelegate, "field").WithArguments("MyStruct.field").WithLocation(7, 52), + // (7,52): error CS8377: The type 'MyStruct>' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'YourStruct' + // public event YourStruct>> field; + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "field").WithArguments("YourStruct", "T", "MyStruct>").WithLocation(7, 52) + ); + + Assert.True(compilation.GetMember("MyStruct").IsManagedTypeNoUseSiteDiagnostics); + Assert.False(compilation.GetMember("YourStruct").IsManagedTypeNoUseSiteDiagnostics); + } + [Fact] public void UnmanagedRecursiveTypeArgumentConstraintViolation_02() { @@ -3717,6 +3872,34 @@ public struct YourStruct where T : unmanaged Assert.True(compilation.GetMember("YourStruct").IsManagedTypeNoUseSiteDiagnostics); } + [Fact] + public void UnmanagedRecursiveTypeArgumentConstraintViolation_03() + { + var code = @" +public struct MyStruct +{ + public YourStruct>> field; +} + +public struct YourStruct where T : unmanaged +{ + public string s; + public T field; +} +"; + var compilation = CreateCompilation(code, options: TestOptions.UnsafeReleaseDll); + compilation.VerifyDiagnostics( + // (4,46): error CS0523: Struct member 'MyStruct.field' of type 'YourStruct>>' causes a cycle in the struct layout + // public YourStruct>> field; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "field").WithArguments("MyStruct.field", "YourStruct>>").WithLocation(4, 46), + // (4,46): error CS8377: The type 'MyStruct>' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'YourStruct' + // public YourStruct>> field; + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "field").WithArguments("YourStruct", "T", "MyStruct>").WithLocation(4, 46)); + + Assert.True(compilation.GetMember("MyStruct").IsManagedTypeNoUseSiteDiagnostics); + Assert.True(compilation.GetMember("YourStruct").IsManagedTypeNoUseSiteDiagnostics); + } + [Fact] public void NestedGenericStructContainingPointer() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 1e55230cdfaa5..fd8620a66a1e8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -5363,15 +5363,9 @@ static void Main() // (7,18): error CS1547: Keyword 'void' cannot be used in this context // d = (ref void () => { }); Diagnostic(ErrorCode.ERR_NoVoidHere, "void").WithLocation(7, 18), - // (7,26): error CS8917: The delegate type could not be inferred. - // d = (ref void () => { }); - Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "=>").WithLocation(7, 26), // (8,27): error CS1547: Keyword 'void' cannot be used in this context // d = (ref readonly void () => { }); - Diagnostic(ErrorCode.ERR_NoVoidHere, "void").WithLocation(8, 27), - // (8,35): error CS8917: The delegate type could not be inferred. - // d = (ref readonly void () => { }); - Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "=>").WithLocation(8, 35)); + Diagnostic(ErrorCode.ERR_NoVoidHere, "void").WithLocation(8, 27)); } [WorkItem(55217, "https://github.com/dotnet/roslyn/issues/55217")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 572f5f8ddd22d..216a495096a2a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -4,7 +4,6 @@ #nullable disable -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -16,7 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public class NameofTests : CSharpTestBase + public sealed class NameofTests : CSharpTestBase { [Fact] public void TestGoodNameofInstances() @@ -249,9 +248,6 @@ class Test // (17,66): error CS1031: Type expected // s = nameof(System.Collections.Generic.Dictionary.KeyCollection); Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(17, 66), - // (11,27): error CS0305: Using the generic type 'Action' requires 1 type arguments - // s = nameof(System.Action<>); - Diagnostic(ErrorCode.ERR_BadArity, "Action<>").WithArguments("System.Action", "type", "1").WithLocation(11, 27), // (16,20): error CS0103: The name 'List' does not exist in the current context // s = nameof(List.Enumerator); Diagnostic(ErrorCode.ERR_NameNotInContext, "List").WithArguments("List").WithLocation(16, 20), @@ -282,9 +278,6 @@ class Test // (31,20): error CS8083: An alias-qualified name is not an expression. // s = nameof(global::Program); // not an expression Diagnostic(ErrorCode.ERR_AliasQualifiedNameNotAnExpression, "global::Program").WithLocation(31, 20), - // (32,20): error CS0305: Using the generic type 'Test' requires 1 type arguments - // s = nameof(Test<>.s); // inaccessible - Diagnostic(ErrorCode.ERR_BadArity, "Test<>").WithArguments("Test", "type", "1").WithLocation(32, 20), // (32,27): error CS0122: 'Test.s' is inaccessible due to its protection level // s = nameof(Test<>.s); // inaccessible Diagnostic(ErrorCode.ERR_BadAccess, "s").WithArguments("Test.s").WithLocation(32, 27), @@ -2373,5 +2366,709 @@ class Attr : System.Attribute { public Attr(string s) {} }"; CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics(expectedDiagnostics); } + + [Fact] + public void OpenTypeInNameof_Preview() + { + CompileAndVerify(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<>); + Console.WriteLine(v); + """, expectedOutput: "List").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_CSharp13() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<>); + Console.WriteLine(v); + """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (4,16): error CS8652: The feature 'unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(List<>); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "List<>").WithArguments("unbound generic types in nameof operator").WithLocation(4, 16)); + } + + [Fact] + public void OpenTypeInNameof_Next() + { + CompileAndVerify(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<>); + Console.WriteLine(v); + """, parseOptions: TestOptions.RegularNext, expectedOutput: "List").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_CSharp13_Nested1() + { + CreateCompilation(""" + using System; + + var v = nameof(A<>.B); + Console.WriteLine(v); + + class A { public class B; } + """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (3,16): error CS8652: The feature 'unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(A<>.B); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "A<>").WithArguments("unbound generic types in nameof operator").WithLocation(3, 16)); + } + + [Fact] + public void OpenTypeInNameof_CSharp13_Nested2() + { + CreateCompilation(""" + using System; + + var v = nameof(A.B<>); + Console.WriteLine(v); + + class A { public class B; } + """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (3,23): error CS8652: The feature 'unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(A.B<>); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "B<>").WithArguments("unbound generic types in nameof operator").WithLocation(3, 23)); + } + + [Fact] + public void OpenTypeInNameof_CSharp13_Nested3() + { + CreateCompilation(""" + using System; + + var v = nameof(A<>.B<>); + Console.WriteLine(v); + + class A { public class B; } + """, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (3,16): error CS8652: The feature 'unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(A<>.B<>); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "A<>").WithArguments("unbound generic types in nameof operator").WithLocation(3, 16), + // (3,20): error CS8652: The feature 'unbound generic types in nameof operator' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var v = nameof(A<>.B<>); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "B<>").WithArguments("unbound generic types in nameof operator").WithLocation(3, 20)); + } + + [Fact] + public void OpenTypeInNameof_BaseCase() + { + CompileAndVerify(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<>); + Console.WriteLine(v); + """, expectedOutput: "List").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_Nested1() + { + CompileAndVerify(""" + using System; + + var v = nameof(A<>.B); + Console.WriteLine(v); + + class A { public class B; } + """, expectedOutput: "B").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_Nested2() + { + CompileAndVerify(""" + using System; + + var v = nameof(A.B<>); + Console.WriteLine(v); + + class A { public class B; } + """, expectedOutput: "B").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_Nested3() + { + CompileAndVerify(""" + using System; + + var v = nameof(A<>.B<>); + Console.WriteLine(v); + + class A { public class B; } + """, expectedOutput: "B").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_MultipleTypeArguments() + { + CompileAndVerify(""" + using System; + using System.Collections.Generic; + + var v = nameof(Dictionary<,>); + Console.WriteLine(v); + """, expectedOutput: "Dictionary").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_IncorrectTypeArgumentCount1() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(Dictionary<>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (2,1): hidden CS8019: Unnecessary using directive. + // using System.Collections.Generic; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System.Collections.Generic;").WithLocation(2, 1), + // (4,16): error CS0305: Using the generic type 'Dictionary' requires 2 type arguments + // var v = nameof(Dictionary<>); + Diagnostic(ErrorCode.ERR_BadArity, "Dictionary<>").WithArguments("System.Collections.Generic.Dictionary", "type", "2").WithLocation(4, 16)); + } + + [Fact] + public void OpenTypeInNameof_IncorrectTypeArgumentCount2() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<,>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (2,1): hidden CS8019: Unnecessary using directive. + // using System.Collections.Generic; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System.Collections.Generic;").WithLocation(2, 1), + // (4,16): error CS0305: Using the generic type 'List' requires 1 type arguments + // var v = nameof(List<,>); + Diagnostic(ErrorCode.ERR_BadArity, "List<,>").WithArguments("System.Collections.Generic.List", "type", "1").WithLocation(4, 16)); + } + + [Fact] + public void OpenTypeInNameof_NoNestedOpenTypes1() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (4,21): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(4, 21)); + } + + [Fact] + public void OpenTypeInNameof_NoNestedOpenTypes2() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List[]>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (4,21): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(4, 21)); + } + + [Fact] + public void OpenTypeInNameof_NoNestedOpenTypes3() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List.Inner>); + Console.WriteLine(v); + + public class Outer { public class Inner { } } + """).VerifyDiagnostics( + // (4,21): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List.Inner>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "Outer<>").WithLocation(4, 21)); + } + + [Fact] + public void OpenTypeInNameof_NoNestedOpenTypes4() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List>); + Console.WriteLine(v); + + public class Outer { public class Inner { } } + """).VerifyDiagnostics( + // (4,27): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "Inner<>").WithLocation(4, 27)); + } + + [Fact] + public void Nameof_NestedClosedType1() + { + CompileAndVerify(""" + using System; + using System.Collections.Generic; + + var v = nameof(List>); + Console.WriteLine(v); + """, expectedOutput: "List").VerifyDiagnostics(); + } + + [Fact] + public void Nameof_NestedClosedType2() + { + CompileAndVerify(""" + using System; + using System.Collections.Generic; + + var v = nameof(List[]>); + Console.WriteLine(v); + """, expectedOutput: "List").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_NoPartialOpenTypes_1() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(Dictionary<,int>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (4,27): error CS1031: Type expected + // var v = nameof(Dictionary<,int>); + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(4, 27)); + } + + [Fact] + public void OpenTypeInNameof_NoPartialOpenTypes_2() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(Dictionary); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (4,31): error CS1031: Type expected + // var v = nameof(Dictionary); + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(4, 31)); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesNotUseTypeArgument() + { + CompileAndVerify(""" + using System; + using System.Collections.Generic; + + var v = nameof(List<>.Count); + Console.WriteLine(v); + """, expectedOutput: "Count").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument() + { + CompileAndVerify(""" + using System; + + var v = nameof(IGoo<>.Count); + Console.WriteLine(v); + + interface IGoo + { + T Count { get; } + } + """, expectedOutput: "Count").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceObjectMember() + { + CompileAndVerify(""" + using System; + + var v = nameof(IGoo<>.Count.ToString); + Console.WriteLine(v); + + interface IGoo + { + T Count { get; } + } + """, expectedOutput: "ToString").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceConstraintMember_Interface() + { + CompileAndVerify(""" + using System; + + var v = nameof(IGoo<>.X.CompareTo); + Console.WriteLine(v); + + interface IGoo where T : IComparable + { + T X { get; } + } + """, expectedOutput: "CompareTo").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceConstraintMember_ThroughTypeParameter() + { + CompileAndVerify(""" + using System; + + var v = nameof(IGoo<,>.X.CompareTo); + Console.WriteLine(v); + + interface IGoo where T : U where U : IComparable + { + T X { get; } + } + """, expectedOutput: "CompareTo").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_MemberAccessThatDoesUseTypeArgument_ReferenceConstraintMember_Class() + { + CompileAndVerify(""" + using System; + + var v = nameof(IGoo<>.X.Z); + Console.WriteLine(v); + + class Base + { + public int Z { get; } + } + + interface IGoo where T : Base + { + T X { get; } + } + """, expectedOutput: "Z").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_GenericMethod1() + { + CompileAndVerify(""" + using System; + + var v = nameof(IGoo.M); + Console.WriteLine(v); + + interface IGoo + { + void M(); + } + """, expectedOutput: "M").VerifyDiagnostics(); + } + + [Fact] + public void OpenTypeInNameof_GenericMethod2() + { + CreateCompilation(""" + using System; + + var v = nameof(IGoo.M<>); + Console.WriteLine(v); + + interface IGoo + { + void M(); + } + """).VerifyDiagnostics( + // (3,16): error CS0305: Using the generic method group 'M' requires 1 type arguments + // var v = nameof(IGoo.M<>); + Diagnostic(ErrorCode.ERR_BadArity, "IGoo.M<>").WithArguments("M", "method group", "1").WithLocation(3, 16)); + } + + [Fact] + public void OpenTypeInNameof_GenericMethod3() + { + CreateCompilation(""" + using System; + + var v = nameof(IGoo.M); + Console.WriteLine(v); + + interface IGoo + { + void M(); + } + """).VerifyDiagnostics( + // (3,16): error CS8084: Type parameters are not allowed on a method group as an argument to 'nameof'. + // var v = nameof(IGoo.M); + Diagnostic(ErrorCode.ERR_NameofMethodGroupWithTypeParameters, "IGoo.M").WithLocation(3, 16)); + } + + [Fact] + public void NameofFunctionPointer1() + { + CreateCompilation(""" + class C + { + unsafe void M() + { + var v = nameof(delegate*); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,32): error CS1514: { expected + // var v = nameof(delegate*); + Diagnostic(ErrorCode.ERR_LbraceExpected, "*").WithLocation(5, 32), + // (5,32): warning CS8848: Operator '*' cannot be used here due to precedence. Use parentheses to disambiguate. + // var v = nameof(delegate*); + Diagnostic(ErrorCode.WRN_PrecedenceInversion, "*").WithArguments("*").WithLocation(5, 32), + // (5,33): error CS1525: Invalid expression term '<' + // var v = nameof(delegate*); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "<").WithArguments("<").WithLocation(5, 33), + // (5,34): error CS1525: Invalid expression term 'int' + // var v = nameof(delegate*); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "int").WithArguments("int").WithLocation(5, 34), + // (5,38): error CS1525: Invalid expression term ')' + // var v = nameof(delegate*); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(5, 38)); + } + + [Fact] + public void NameofFunctionPointer2() + { + CreateCompilation(""" + using System.Collections.Generic; + + class C + { + unsafe void M() + { + var v = nameof(delegate*>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,32): error CS1514: { expected + // var v = nameof(delegate*>); + Diagnostic(ErrorCode.ERR_LbraceExpected, "*").WithLocation(7, 32), + // (7,32): warning CS8848: Operator '*' cannot be used here due to precedence. Use parentheses to disambiguate. + // var v = nameof(delegate*>); + Diagnostic(ErrorCode.WRN_PrecedenceInversion, "*").WithArguments("*").WithLocation(7, 32), + // (7,33): error CS1525: Invalid expression term '<' + // var v = nameof(delegate*>); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "<").WithArguments("<").WithLocation(7, 33), + // (7,34): error CS0119: 'List' is a type, which is not valid in the given context + // var v = nameof(delegate*>); + Diagnostic(ErrorCode.ERR_BadSKunknown, "List<>").WithArguments("System.Collections.Generic.List", "type").WithLocation(7, 34), + // (7,41): error CS1525: Invalid expression term ')' + // var v = nameof(delegate*>); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(7, 41)); + } + + [Fact] + public void NameofFunctionPointer3() + { + CreateCompilation(""" + using System.Collections.Generic; + + class C + { + unsafe void M() + { + var v = nameof(List>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,13): warning CS0219: The variable 'v' is assigned but its value is never used + // var v = nameof(List>); + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "v").WithArguments("v").WithLocation(7, 13), + // (7,29): error CS0306: The type 'delegate*' may not be used as a type argument + // var v = nameof(List>); + Diagnostic(ErrorCode.ERR_BadTypeArgument, "delegate*").WithArguments("delegate*").WithLocation(7, 29)); + } + + [Fact] + public void NameofFunctionPointer4() + { + CreateCompilation(""" + using System.Collections.Generic; + + class C + { + unsafe void M() + { + var v = nameof(List>>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,13): warning CS0219: The variable 'v' is assigned but its value is never used + // var v = nameof(List>>); + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "v").WithArguments("v").WithLocation(7, 13), + // (7,29): error CS0306: The type 'delegate*>' may not be used as a type argument + // var v = nameof(List>>); + Diagnostic(ErrorCode.ERR_BadTypeArgument, "delegate*>").WithArguments("delegate*>").WithLocation(7, 29), + // (7,39): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List>>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(7, 39)); + } + + [Fact] + public void NameofFunctionPointer5() + { + CreateCompilation(""" + using System.Collections.Generic; + + class D + { + unsafe void M() + { + var v = nameof(D<, delegate*, List<>>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,26): error CS1031: Type expected + // var v = nameof(D<, delegate*, List<>>); + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(7, 26), + // (7,44): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(D<, delegate*, List<>>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(7, 44)); + } + + [Fact] + public void Nameof_NestedOpenType1() + { + CompileAndVerify(""" + using System; + using System.Collections.Generic; + + var v = nameof(List[]>); + Console.WriteLine(v); + """, expectedOutput: "List").VerifyDiagnostics(); + } + + [Fact] + public void Nameof_NestedOpenType2() + { + CreateCompilation(""" + using System; + using System.Collections.Generic; + + var v = nameof(List[]>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (4,21): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List[]>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(4, 21)); + } + + [Fact] + public void Nameof_NestedOpenType3() + { + CompileAndVerify(""" + #nullable enable + using System; + using System.Collections.Generic; + + var v = nameof(List?>); + Console.WriteLine(v); + """, expectedOutput: "List").VerifyDiagnostics(); + } + + [Fact] + public void Nameof_NestedOpenType4() + { + CreateCompilation(""" + #nullable enable + using System; + using System.Collections.Generic; + + var v = nameof(List?>); + Console.WriteLine(v); + """).VerifyDiagnostics( + // (5,21): error CS7003: Unexpected use of an unbound generic name + // var v = nameof(List?>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(5, 21)); + } + + [Fact] + public void Nameof_AliasQualifiedName() + { + CompileAndVerify(""" + using System; + + var v = nameof(global::System.Collections.Generic.List<>); + Console.WriteLine(v); + """, expectedOutput: "List").VerifyDiagnostics(); + } + + [Theory] + [InlineData("IGoo<>")] + [InlineData("IGoo<>.Count")] + public void OpenTypeInNameof_SemanticModelTest1(string nameofTypeString) + { + var compilation = CreateCompilation($$""" + using System; + + var v1 = nameof({{nameofTypeString}}); + var v2 = typeof(IGoo<>); + Console.WriteLine(v1 + v2); + + interface IGoo { public T Count { get; } } + """).VerifyDiagnostics(); + var tree = compilation.SyntaxTrees.Single(); + var semanticModel = compilation.GetSemanticModel(tree); + + var root = tree.GetRoot(); + + var firstGeneric = root.DescendantNodes().OfType().First(); + var lastGeneric = root.DescendantNodes().OfType().Last(); + + Assert.NotSame(firstGeneric, lastGeneric); + + // Ensure the type inside the nameof is the same as the type inside the typeof. + var nameofType = semanticModel.GetTypeInfo(firstGeneric).Type; + var typeofType = semanticModel.GetTypeInfo(lastGeneric).Type; + + Assert.NotNull(nameofType); + Assert.NotNull(typeofType); + + // typeof will produce IGoo<>, while nameof will produce IGoo. These are distinctly different types (the + // latter has members for example). + Assert.NotEqual(nameofType, typeofType); + + Assert.True(nameofType.IsDefinition); + Assert.False(nameofType.IsUnboundGenericType()); + + Assert.False(typeofType.IsDefinition); + Assert.True(typeofType.IsUnboundGenericType()); + + Assert.Empty(typeofType.GetMembers("Count")); + Assert.Single(nameofType.GetMembers("Count")); + + var igooType = compilation.GetTypeByMetadataName("IGoo`1").GetPublicSymbol(); + Assert.NotNull(igooType); + + Assert.Equal(igooType, nameofType); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index e2a7bc8b97f0d..3a4c805f23882 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -20937,10 +20937,91 @@ public C() ); } - [Fact] - public void MemberNotNull_LocalFunction() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceConstructor_InstanceFields() { - var c = CreateNullableCompilation(new[] { @" + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1; + public string? field2; + public string field3; + public string? field4; + + public C() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); // 1 + field4.ToString(); // 2 + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() => throw null!; + } +} +""", MemberNotNullAttributeDefinition], parseOptions: TestOptions.Regular9); + + c.VerifyDiagnostics( + // (14,9): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(14, 9), + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceConstructor_StaticFields() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public static string field1; // 1 + public static string? field2; + public static string field3; // 2 + public static string? field4; + + public C() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); + field4.ToString(); // 3 + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + } // 4, 5 + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (4,26): warning CS8618: Non-nullable field 'field1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. + // public static string field1; // 1 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "field1").WithArguments("field", "field1").WithLocation(4, 26), + // (6,26): warning CS8618: Non-nullable field 'field3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. + // public static string field3; // 2 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "field3").WithArguments("field", "field3").WithLocation(6, 26), + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9), + // (20,9): warning CS8774: Member 'field1' must have a non-null value when exiting. + // } // 4, 5 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field1").WithLocation(20, 9), + // (20,9): warning CS8774: Member 'field2' must have a non-null value when exiting. + // } // 4, 5 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field2").WithLocation(20, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinInstanceConstructor_InstanceFields() + { + var c = CreateNullableCompilation([""" using System.Diagnostics.CodeAnalysis; public class C { @@ -20958,26 +21039,1709 @@ public C() field4.ToString(); // 4 [MemberNotNull(nameof(field1), nameof(field2))] - void init() => throw null!; + static void init() + { + } } } -", MemberNotNullAttributeDefinition }, parseOptions: TestOptions.Regular9); +""", MemberNotNullAttributeDefinition]); - // Note: the local function is not invoked on this or base c.VerifyDiagnostics( - // (13,9): warning CS8602: Dereference of a possibly null reference. + // (12,9): warning CS8602: Dereference of a possibly null reference. // field1.ToString(); // 1 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(13, 9), - // (14,9): warning CS8602: Dereference of a possibly null reference. + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(12, 9), + // (13,9): warning CS8602: Dereference of a possibly null reference. // field2.ToString(); // 2 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(14, 9), + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(13, 9), + // (14,9): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(14, 9), + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 4 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinInstanceConstructor_StaticFields() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public static string field1; // 1 + public static string? field2; + public static string field3; // 2 + public static string? field4; + + public C() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); + field4.ToString(); // 3 + + [MemberNotNull(nameof(field1), nameof(field2))] + static void init() + { + } // 4, 5 + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (4,26): warning CS8618: Non-nullable field 'field1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. + // public static string field1; // 1 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "field1").WithArguments("field", "field1").WithLocation(4, 26), + // (6,26): warning CS8618: Non-nullable field 'field3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. + // public static string field3; // 2 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "field3").WithArguments("field", "field3").WithLocation(6, 26), + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9), + // (20,9): warning CS8774: Member 'field1' must have a non-null value when exiting. + // } // 4, 5 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field1").WithLocation(20, 9), + // (20,9): warning CS8774: Member 'field2' must have a non-null value when exiting. + // } // 4, 5 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field2").WithLocation(20, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinStaticConstructor_StaticFields() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public static string field1; + public static string? field2; + public static string field3; + public static string? field4; + + static C() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); // 1 + field4.ToString(); // 2 + + [MemberNotNull(nameof(field1), nameof(field2))] + static void init() + { + } // 3, 4 + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (14,9): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(14, 9), + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9), + // (20,9): warning CS8774: Member 'field1' must have a non-null value when exiting. + // } // 3, 4 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field1").WithLocation(20, 9), + // (20,9): warning CS8774: Member 'field2' must have a non-null value when exiting. + // } // 3, 4 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field2").WithLocation(20, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_MissingArgument() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2 = null; + public int capture; + + public void M() + { + init(); + field1.ToString(); + field2.ToString(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init(int a) { System.Console.Write(capture); field1 = ""; field2 = ""; } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (10,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'init(int)' + // init(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "init").WithArguments("a", "init(int)").WithLocation(10, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_BaseField() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class Base +{ + public string field1; + public string? field2; + public string field3; + public string? field4; +} +public class C : Base +{ + public C() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); + field4.ToString(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() => throw null!; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (4,19): warning CS8618: Non-nullable field 'field1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. + // public string field1; + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "field1").WithArguments("field", "field1").WithLocation(4, 19), + // (6,19): warning CS8618: Non-nullable field 'field3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. + // public string field3; + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "field3").WithArguments("field", "field3").WithLocation(6, 19), + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(15, 9), + // (17,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(17, 9), + // (19,10): warning CS8776: Member 'field1' cannot be used in this attribute. + // [MemberNotNull(nameof(field1), nameof(field2))] + Diagnostic(ErrorCode.WRN_MemberNotNullBadMember, "MemberNotNull(nameof(field1), nameof(field2))").WithArguments("field1").WithLocation(19, 10), + // (19,10): warning CS8776: Member 'field2' cannot be used in this attribute. + // [MemberNotNull(nameof(field1), nameof(field2))] + Diagnostic(ErrorCode.WRN_MemberNotNullBadMember, "MemberNotNull(nameof(field1), nameof(field2))").WithArguments("field2").WithLocation(19, 10)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceMethod() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); + field4.ToString(); // 1 + + [MemberNotNull(nameof(field1), nameof(field2))] + bool init() => throw null!; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( // (15,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceMethod_Generic() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(new object()); + field1.ToString(); + field2.ToString(); + field3.ToString(); + field4.ToString(); // 1 + + [MemberNotNull(nameof(field1), nameof(field2))] + bool init(T t) => throw null!; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceMethod_Nested() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + local(); + + void local() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); + field4.ToString(); // 1 + + [MemberNotNull(nameof(field1), nameof(field2))] + bool init() => throw null!; + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (19,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(19, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceMethod_Nested_WithinStaticLocalFunction() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + local(); + + static void local() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); + field4.ToString(); + + [MemberNotNull(nameof(field1), nameof(field2))] + bool init() => throw null!; + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (16,13): error CS8422: A static local function cannot contain a reference to 'this' or 'base'. + // field1.ToString(); + Diagnostic(ErrorCode.ERR_StaticLocalFunctionCannotCaptureThis, "field1").WithLocation(16, 13), + // (17,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(17, 13), + // (17,13): error CS8422: A static local function cannot contain a reference to 'this' or 'base'. + // field2.ToString(); + Diagnostic(ErrorCode.ERR_StaticLocalFunctionCannotCaptureThis, "field2").WithLocation(17, 13), + // (18,13): error CS8422: A static local function cannot contain a reference to 'this' or 'base'. + // field3.ToString(); + Diagnostic(ErrorCode.ERR_StaticLocalFunctionCannotCaptureThis, "field3").WithLocation(18, 13), + // (19,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(19, 13), + // (19,13): error CS8422: A static local function cannot contain a reference to 'this' or 'base'. + // field4.ToString(); + Diagnostic(ErrorCode.ERR_StaticLocalFunctionCannotCaptureThis, "field4").WithLocation(19, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceMethod_Nested_WithinStaticLocalFunction_NotSet() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + local(); + + static void local() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + } + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceMethod_Nested_WithinLocalFunctionWithoutMemberNotNull() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + + public void M() + { + field1.ToString(); + field2.ToString(); // 1 + local(); + + void local() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + bool init() => throw null!; + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (10,9): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(10, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinInstanceMethod() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + + public void M() + { + init(); + field1.ToString(); + field2.ToString(); // 1 + + [MemberNotNull(nameof(field1), nameof(field2))] + static bool init() => throw null!; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (11,9): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(11, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinInstanceMethod_NotSet() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + + public void M() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + static void init() + { + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinStaticMethod() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public static class C +{ + public static string field1 = ""; + public static string? field2; + public static string field3 = ""; + public static string? field4; + + public static void M() + { + init(); + field1.ToString(); + field2.ToString(); + field3.ToString(); + field4.ToString(); // 1 + + [MemberNotNull(nameof(field1), nameof(field2))] + static bool init() => throw null!; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (15,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinInstanceMethod_StaticFields() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public static string field1 = ""; + public static string? field2; + + public void M() + { + init(); + field1.ToString(); + field2.ToString(); + + [MemberNotNull(nameof(field1), nameof(field2))] + static bool init() => throw null!; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinInstanceMethod_InstanceFields() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + + public void M() + { + init(); + field1.ToString(); + field2.ToString(); // 1 + + [MemberNotNull(nameof(field1), nameof(field2))] + static bool init() => throw null!; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (11,9): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(11, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_WithinInstanceMethod_InstanceFields_NotSet() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + + public void M() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + static void init() + { + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceMethod_StaticFields() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public static string field1 = ""; + public static string? field2; + + public void M() + { + init(); + field1.ToString(); + field2.ToString(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() => throw null!; + } + + public void M2() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + field1.ToString(); // 1 + field2.ToString(); // 2 + } + } + + public void M3() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + } // 3, 4 + } + + public void M4() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + field1 = ""; + field2 = ""; + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (24,13): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(24, 13), + // (25,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(25, 13), + // (36,9): warning CS8774: Member 'field1' must have a non-null value when exiting. + // } // 3, 4 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field1").WithLocation(36, 9), + // (36,9): warning CS8774: Member 'field2' must have a non-null value when exiting. + // } // 3, 4 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field2").WithLocation(36, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinInstanceAccessor_StaticFields() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public static string field1 = ""; + public static string? field2; + + public int Property1 + { + get + { + init(); + field1.ToString(); + field2.ToString(); + return 0; + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() => throw null!; + } + } + + public int Property2 + { + get + { + init(); + return 0; + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + field1.ToString(); // 1 + field2.ToString(); // 2 + } + } + } + + public int Property3 + { + get + { + init(); + return 0; + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + } // 3, 4 + } + } + + public int Property4 + { + get + { + init(); + return 0; + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + field1 = ""; + field2 = ""; + } + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (31,17): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(31, 17), + // (32,17): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(32, 17), + // (47,13): warning CS8774: Member 'field1' must have a non-null value when exiting. + // } // 3, 4 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field1").WithLocation(47, 13), + // (47,13): warning CS8774: Member 'field2' must have a non-null value when exiting. + // } // 3, 4 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field2").WithLocation(47, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinStaticMethod() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public static string field1 = ""; + public static string? field2; + + public static void M() + { + init(); + field1.ToString(); + field2.ToString(); + + [MemberNotNull(nameof(field1), nameof(field2))] + bool init() => throw null!; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinLambda() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1; + public string? field2; + public string field3; + public string? field4; + + public C() + { + System.Action a = () => + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() => throw null!; + }; + + field1.ToString(); // 1 + field2.ToString(); // 2 + field3.ToString(); // 3 + field4.ToString(); // 4 + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (19,9): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(19, 9), + // (20,9): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(20, 9), + // (21,9): warning CS8602: Dereference of a possibly null reference. // field3.ToString(); // 3 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(15, 9), - // (16,9): warning CS8602: Dereference of a possibly null reference. + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(21, 9), + // (22,9): warning CS8602: Dereference of a possibly null reference. // field4.ToString(); // 4 - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(16, 9) - ); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(22, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_WithinAnonymousFunction() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1; + public string? field2; + public string field3; + public string? field4; + + public C() + { + System.Action a = delegate() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() => throw null!; + }; + + field1.ToString(); // 1 + field2.ToString(); // 2 + field3.ToString(); // 3 + field4.ToString(); // 4 + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (19,9): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(19, 9), + // (20,9): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(20, 9), + // (21,9): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(21, 9), + // (22,9): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 4 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(22, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_TopLevel() + { + var c = CreateCompilation([""" +#nullable enable +using System.Diagnostics.CodeAnalysis; + +init(); + +[MemberNotNull(nameof(field1))] +void init() => throw null!; +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (6,23): error CS0103: The name 'field1' does not exist in the current context + // [MemberNotNull(nameof(field1))] + Diagnostic(ErrorCode.ERR_NameNotInContext, "field1").WithArguments("field1").WithLocation(6, 23)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NonStaticLocalFunction_TopLevel_WithField() + { + var c = CreateCompilation([""" +#nullable enable +using System.Diagnostics.CodeAnalysis; + +init(); +field1.ToString(); + +[MemberNotNull(nameof(field1))] +void init() => throw null!; + +partial class Program +{ + public static object? field1 = null; +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_TopLevel() + { + var c = CreateCompilation([""" +#nullable enable +using System.Diagnostics.CodeAnalysis; + +init(); + +[MemberNotNull(nameof(field1))] +static void init() => throw null!; +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (6,23): error CS0103: The name 'field1' does not exist in the current context + // [MemberNotNull(nameof(field1))] + Diagnostic(ErrorCode.ERR_NameNotInContext, "field1").WithArguments("field1").WithLocation(6, 23)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_StaticLocalFunction_TopLevel_WithField() + { + var c = CreateCompilation([""" +#nullable enable +using System.Diagnostics.CodeAnalysis; + +init(); +field1.ToString(); +field1 = ""; + +[MemberNotNull(nameof(field1))] +static void init() => throw null!; + +partial class Program +{ + public static object? field1; +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_EnforcedInLocalFunction_SetToMaybeNull() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + field1.ToString(); // 1 + field2.ToString(); // 2 + field3.ToString(); + field4.ToString(); // 3 + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (16,13): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(16, 13), + // (17,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(17, 13), + // (19,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(19, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NotEnforcedInLambda_SetToMaybeNull() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + var lambda = [MemberNotNull(nameof(field1), nameof(field2))] void () => + { + field1.ToString(); + field2.ToString(); // 1 + field3.ToString(); + field4.ToString(); // 2 + }; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (14,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(14, 13), + // (16,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(16, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_EnforcedInLocalFunction_Property_SetToMaybeNull() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string P1 { get; set; } = ""; + public string? P2 { get; set; } + public string P3 { get; set; } = ""; + public string? P4 { get; set; } + + public void M() + { + init(); + + [MemberNotNull(nameof(P1), nameof(P2))] + void init() + { + P1.ToString(); // 1 + P2.ToString(); // 2 + P3.ToString(); + P4.ToString(); // 3 + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (16,13): warning CS8602: Dereference of a possibly null reference. + // P1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "P1").WithLocation(16, 13), + // (17,13): warning CS8602: Dereference of a possibly null reference. + // P2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "P2").WithLocation(17, 13), + // (19,13): warning CS8602: Dereference of a possibly null reference. + // P4.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "P4").WithLocation(19, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_EnforcedInLocalFunction_CheckNotNullOnExit() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + } // 1 + } +} +""", MemberNotNullAttributeDefinition]); + + // Note: we add a synthesized BoundReturnStatement in BindLocalFunctionStatement + // using the block body as syntax, but we report the diagnostic on the closing brace in that case + c.VerifyEmitDiagnostics( + // (16,9): warning CS8774: Member 'field1' must have a non-null value when exiting. + // } // 1 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field1").WithLocation(16, 9), + // (16,9): warning CS8774: Member 'field2' must have a non-null value when exiting. + // } // 1 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field2").WithLocation(16, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_EnforcedInLocalFunction_CheckNotNullOnExit_WithExplicitReturn() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init() + { + return; + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (16,13): warning CS8774: Member 'field1' must have a non-null value when exiting. + // return; + Diagnostic(ErrorCode.WRN_MemberNotNull, "return;").WithArguments("field1").WithLocation(16, 13), + // (16,13): warning CS8774: Member 'field2' must have a non-null value when exiting. + // return; + Diagnostic(ErrorCode.WRN_MemberNotNull, "return;").WithArguments("field2").WithLocation(16, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_EnforcedInLocalFunction_CheckNotNullOnExit_InOneBranch() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(true); + + [MemberNotNull(nameof(field1), nameof(field2))] + void init(bool b) + { + if (b) + { + field1 = ""; + field2 = ""; + return; + } + else + { + return; // 1 + } + } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (24,17): warning CS8774: Member 'field1' must have a non-null value when exiting. + // return; // 1 + Diagnostic(ErrorCode.WRN_MemberNotNull, "return;").WithArguments("field1").WithLocation(24, 17), + // (24,17): warning CS8774: Member 'field2' must have a non-null value when exiting. + // return; // 1 + Diagnostic(ErrorCode.WRN_MemberNotNull, "return;").WithArguments("field2").WithLocation(24, 17)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_EnforcedInLocalFunction_CheckNotNullOnExit_MissingReturn() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(); + + [MemberNotNull(nameof(field1), nameof(field2))] + int init() + { + } // 1 + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (14,13): error CS0161: 'init()': not all code paths return a value + // int init() + Diagnostic(ErrorCode.ERR_ReturnExpected, "init").WithArguments("init()").WithLocation(14, 13), + // (16,9): warning CS8774: Member 'field1' must have a non-null value when exiting. + // } // 1 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field1").WithLocation(16, 9), + // (16,9): warning CS8774: Member 'field2' must have a non-null value when exiting. + // } // 1 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field2").WithLocation(16, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_NotEnforcedInLambda_CheckNullOnExit() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + var lambda = [MemberNotNull(nameof(field1), nameof(field2))] void () => { }; + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNull_InLocalFunction_BadFields() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class Base +{ + public object? field3; +} +public class C : Base +{ + [MemberNotNull("field1")] + public void M() + { + init(); + + [MemberNotNull("field2", nameof(field3))] + void init() { } + } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (8,6): warning CS8776: Member 'field1' cannot be used in this attribute. + // [MemberNotNull("field1")] + Diagnostic(ErrorCode.WRN_MemberNotNullBadMember, @"MemberNotNull(""field1"")").WithArguments("field1").WithLocation(8, 6), + // (13,10): warning CS8776: Member 'field2' cannot be used in this attribute. + // [MemberNotNull("field2", nameof(field3))] + Diagnostic(ErrorCode.WRN_MemberNotNullBadMember, @"MemberNotNull(""field2"", nameof(field3))").WithArguments("field2").WithLocation(13, 10), + // (13,10): warning CS8776: Member 'field3' cannot be used in this attribute. + // [MemberNotNull("field2", nameof(field3))] + Diagnostic(ErrorCode.WRN_MemberNotNullBadMember, @"MemberNotNull(""field2"", nameof(field3))").WithArguments("field3").WithLocation(13, 10)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNullWhenTrue_NonStaticLocalFunction() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1; + public string? field2; + public string field3; + public string? field4; + + public C() + { + if (init()) + { + field1.ToString(); + field2.ToString(); + field3.ToString(); // 1 + field4.ToString(); // 2 + } + else + { + field1.ToString(); // 3 + field2.ToString(); // 4 + field3.ToString(); // 5 + field4.ToString(); // 6 + } + + [MemberNotNullWhen(true, nameof(field1), nameof(field2))] + bool init() => throw null!; + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyDiagnostics( + // (15,13): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(15, 13), + // (16,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(16, 13), + // (20,13): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(20, 13), + // (21,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 4 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(21, 13), + // (22,13): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 5 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(22, 13), + // (23,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 6 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(23, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNullWhenTrue_StaticLocalFunction() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public static class C +{ + public static string field1; + public static string? field2; + public static string field3; + public static string? field4; + + static C() + { + if (init()) + { + field1.ToString(); + field2.ToString(); + field3.ToString(); // 1 + field4.ToString(); // 2 + } + else + { + field1.ToString(); // 3 + field2.ToString(); // 4 + field3.ToString(); // 5 + field4.ToString(); // 6 + } + + [MemberNotNullWhen(true, nameof(field1), nameof(field2))] + static bool init() => throw null!; + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyDiagnostics( + // (15,13): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(15, 13), + // (16,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(16, 13), + // (20,13): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(20, 13), + // (21,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 4 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(21, 13), + // (22,13): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 5 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(22, 13), + // (23,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 6 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(23, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNullWhenFalse_NonStaticLocalFunction() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1; + public string? field2; + public string field3; + public string? field4; + + public C() + { + if (init()) + { + field1.ToString(); // 1 + field2.ToString(); // 2 + field3.ToString(); // 3 + field4.ToString(); // 4 + } + else + { + field1.ToString(); + field2.ToString(); + field3.ToString(); // 5 + field4.ToString(); // 6 + } + + [MemberNotNullWhen(false, nameof(field1), nameof(field2))] + bool init() => throw null!; + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyDiagnostics( + // (13,13): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(13, 13), + // (14,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(14, 13), + // (15,13): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(15, 13), + // (16,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 4 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(16, 13), + // (22,13): warning CS8602: Dereference of a possibly null reference. + // field3.ToString(); // 5 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field3").WithLocation(22, 13), + // (23,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 6 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(23, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNullWhenTrue_EnforcedInLocalFunction_SetToMaybeNull() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(); + + [MemberNotNullWhen(true, nameof(field1), nameof(field2))] + bool init() + { + field1.ToString(); // 1 + field2.ToString(); // 2 + field3.ToString(); + field4.ToString(); // 3 + return true; + } + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (16,13): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(16, 13), + // (17,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(17, 13), + // (19,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(19, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNullWhenFalse_EnforcedInLocalFunction_SetToMaybeNull() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(); + + [MemberNotNullWhen(false, nameof(field1), nameof(field2))] + bool init() + { + field1.ToString(); // 1 + field2.ToString(); // 2 + field3.ToString(); + field4.ToString(); // 3 + return true; + } + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (16,13): warning CS8602: Dereference of a possibly null reference. + // field1.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field1").WithLocation(16, 13), + // (17,13): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(17, 13), + // (19,13): warning CS8602: Dereference of a possibly null reference. + // field4.ToString(); // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(19, 13)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNullWhenTrue_EnforcedInLocalFunction_CheckNotNullOnExit() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(true); + + [MemberNotNullWhen(true, nameof(field1), nameof(field2))] + bool init(bool b) + { + if (b) + { + return true; // 1 + } + + return false; + } + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (18,17): warning CS8775: Member 'field1' must have a non-null value when exiting with 'true'. + // return true; // 1 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return true;").WithArguments("field1", "true").WithLocation(18, 17), + // (18,17): warning CS8775: Member 'field2' must have a non-null value when exiting with 'true'. + // return true; // 1 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return true;").WithArguments("field2", "true").WithLocation(18, 17)); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNullWhenTrueOrFalse_EnforcedInLocalFunction_CheckNotNullOnExit_Set(bool sense) + { + var c = CreateNullableCompilation([$$""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + + public void M() + { + init(true); + + [MemberNotNullWhen({{(sense ? "true" : "false")}}, nameof(field1), nameof(field2))] + bool init(bool b) + { + if (b) + { + field1 = ""; + field2 = ""; + return true; + } + + field1 = ""; + field2 = ""; + return false; + } + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyEmitDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] + public void MemberNotNullWhenFalse_EnforcedInLocalFunction_CheckNotNullOnExit() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1 = ""; + public string? field2; + public string field3 = ""; + public string? field4; + + public void M() + { + init(true); + + [MemberNotNullWhen(false, nameof(field1), nameof(field2))] + bool init(bool b) + { + if (b) + { + return true; + } + + return false; // 1 + } + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyEmitDiagnostics( + // (21,13): warning CS8775: Member 'field1' must have a non-null value when exiting with 'false'. + // return false; // 1 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return false;").WithArguments("field1", "false").WithLocation(21, 13), + // (21,13): warning CS8775: Member 'field2' must have a non-null value when exiting with 'false'. + // return false; // 1 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return false;").WithArguments("field2", "false").WithLocation(21, 13)); + } + + [Fact] + public void MemberNotNullWhenTrue_EnforcedInLocalFunction_CheckNotNullOnExit_NonConstant() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1; + public string field3; + + C() + { + if (!init()) + throw null!; + + [MemberNotNullWhen(true, nameof(field1), nameof(field3))] + bool init() + { + bool b = true; + if (b) + { + return field1 == null; // 1 + } + if (b) + { + return field1 != null; + } + if (b) + { + return init(); + } + return !init(); // 2, 3 + } + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyDiagnostics( + // (18,17): warning CS8775: Member 'field1' must have a non-null value when exiting with 'true'. + // return field1 == null; // 1 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return field1 == null;").WithArguments("field1", "true").WithLocation(18, 17), + // (28,13): warning CS8775: Member 'field1' must have a non-null value when exiting with 'true'. + // return !init(); // 2, 3 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return !init();").WithArguments("field1", "true").WithLocation(28, 13), + // (28,13): warning CS8775: Member 'field3' must have a non-null value when exiting with 'true'. + // return !init(); // 2, 3 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return !init();").WithArguments("field3", "true").WithLocation(28, 13)); + } + + [Fact] + public void MemberNotNullWhenFalse_EnforcedInLocalFunction_CheckNotNullOnExit_NonConstant() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; +public class C +{ + public string field1; + public string field3; + + C() + { + if (init()) + throw null!; + + [MemberNotNullWhen(false, nameof(field1), nameof(field3))] + bool init() + { + bool b = true; + if (b) + { + return field1 == null; + } + if (b) + { + return field1 != null; // 1 + } + if (b) + { + return init(); + } + return !init(); // 2, 3 + } + } +} +""", MemberNotNullWhenAttributeDefinition]); + + c.VerifyDiagnostics( + // (22,17): warning CS8775: Member 'field1' must have a non-null value when exiting with 'false'. + // return field1 != null; // 1 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return field1 != null;").WithArguments("field1", "false").WithLocation(22, 17), + // (28,13): warning CS8775: Member 'field1' must have a non-null value when exiting with 'false'. + // return !init(); // 2, 3 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return !init();").WithArguments("field1", "false").WithLocation(28, 13), + // (28,13): warning CS8775: Member 'field3' must have a non-null value when exiting with 'false'. + // return !init(); // 2, 3 + Diagnostic(ErrorCode.WRN_MemberNotNullWhen, "return !init();").WithArguments("field3", "false").WithLocation(28, 13)); } [Fact] @@ -23216,6 +24980,29 @@ void M2(C c) ); } + [Fact, WorkItem(47667, "https://github.com/dotnet/roslyn/issues/47667")] + public void MemberNotNull_Extension_02() + { + var c = CreateNullableCompilation([""" +using System.Diagnostics.CodeAnalysis; + +public class C { } + +public static class Ext +{ + public static string? _field; + + [MemberNotNull("_field")] + public static void AssertFieldNotNull(this C c) { } +} +""", MemberNotNullAttributeDefinition]); + + c.VerifyDiagnostics( + // (10,55): warning CS8774: Member '_field' must have a non-null value when exiting. + // public static void AssertFieldNotNull(this C c) { } + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("_field").WithLocation(10, 55)); + } + [Fact, WorkItem(47667, "https://github.com/dotnet/roslyn/issues/47667")] public void MemberNotNullWhen_Extension_01() { @@ -90888,7 +92675,7 @@ static class Extensions comp.VerifyDiagnostics(); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75430")] public void Foreach_NullableElementType_Suppression() { var source = """ @@ -90896,7 +92683,7 @@ public void Foreach_NullableElementType_Suppression() class C { void M1(object?[] a) { foreach (object item in a) { } } - void M2(object[] a) { foreach (object item in a!) { } } + void M2(object?[] a) { foreach (object item in a!) { } } } """; CreateCompilation(source).VerifyDiagnostics( @@ -154252,7 +156039,7 @@ public void MemberNotNull_InstanceMemberOnStaticMethod() class C { public string? field; - + [MemberNotNull(""field"")] public static void M() { @@ -154278,6 +156065,67 @@ public void Test() Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field").WithLocation(15, 9)); } + [Fact] + public void MemberNotNull_StaticMemberOnInstanceMethod() + { + var source = +@"using System.Diagnostics.CodeAnalysis; + +class C +{ + public static string? field; + + [MemberNotNull(""field"")] + public void M() + { + } + + public void Test() + { + M(); + field.ToString(); + } +}"; + var comp = CreateCompilation([source], options: WithNullableEnable(), targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (5,27): warning CS0649: Field 'C.field' is never assigned to, and will always have its default value null + // public static string? field; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "field").WithArguments("C.field", "null").WithLocation(5, 27), + // (10,5): warning CS8774: Member 'field' must have a non-null value when exiting. + // } + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field").WithLocation(10, 5)); + } + + [Fact] + public void MemberNotNull_StaticMemberOnInstanceMethod_Assigned() + { + var source = """ +using System.Diagnostics.CodeAnalysis; + +class C +{ + public static string? field; + + [MemberNotNull("field")] + public void M() + { + field = ""; + } + + [MemberNotNull("field")] + public void M2() + { + field.ToString(); // 1 + } +} +"""; + var comp = CreateCompilation([source], options: WithNullableEnable(), targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (16,9): warning CS8602: Dereference of a possibly null reference. + // field.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field").WithLocation(16, 9)); + } + [Fact, WorkItem(39417, "https://github.com/dotnet/roslyn/issues/39417")] public void CustomAwaitable_DetectSettingNullableToNonNullableType() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs index ece862f515b6c..1dcadde013a81 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs @@ -1006,18 +1006,21 @@ public static void Main() } "; string expectedOperationTree = @" -IObjectCreationOperation (Constructor: MemberInitializerTest..ctor()) (OperationKind.ObjectCreation, Type: MemberInitializerTest, IsInvalid) (Syntax: 'new MemberI ... z = null }') + IObjectCreationOperation (Constructor: MemberInitializerTest..ctor()) (OperationKind.ObjectCreation, Type: MemberInitializerTest, IsInvalid) (Syntax: 'new MemberI ... z = null }') Arguments(0) - Initializer: + Initializer: IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: MemberInitializerTest, IsInvalid) (Syntax: '{ z = null }') Initializers(1): ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: D, IsInvalid) (Syntax: 'z = null') - Left: + Left: IEventReferenceOperation: event D MemberInitializerTest.z (OperationKind.EventReference, Type: D, IsInvalid) (Syntax: 'z') - Instance Receiver: + Instance Receiver: IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: MemberInitializerTest, IsInvalid, IsImplicit) (Syntax: 'z') - Right: - ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: D, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0070: The event 'MemberInitializerTest.z' can only appear on the left hand side of += or -= (except when used from within the type 'MemberInitializerTest') @@ -1818,16 +1821,19 @@ public static void Main() string expectedOperationTree = @" IObjectCreationOperation (Constructor: X..ctor()) (OperationKind.ObjectCreation, Type: X, IsInvalid) (Syntax: 'new X() { x = 0 }') Arguments(0) - Initializer: + Initializer: IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: X, IsInvalid) (Syntax: '{ x = 0 }') Initializers(1): ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: X, IsInvalid) (Syntax: 'x = 0') - Left: + Left: IFieldReferenceOperation: X.x (Static) (OperationKind.FieldReference, Type: X, IsInvalid) (Syntax: 'x') - Instance Receiver: + Instance Receiver: null - Right: - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: X, Constant: 0, IsImplicit) (Syntax: '0') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1914: Static field or property 'X.x' cannot be assigned in an object initializer @@ -1857,22 +1863,22 @@ public static void Main() string expectedOperationTree = @" IObjectCreationOperation (Constructor: MemberInitializerTest..ctor()) (OperationKind.ObjectCreation, Type: MemberInitializerTest, IsInvalid) (Syntax: 'new MemberI ... Prop = 1 }') Arguments(0) - Initializer: + Initializer: IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: MemberInitializerTest, IsInvalid) (Syntax: '{ x = 1, Prop = 1 }') Initializers(2): ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsInvalid) (Syntax: 'x = 1') - Left: + Left: IFieldReferenceOperation: System.Int32 MemberInitializerTest.x (Static) (OperationKind.FieldReference, Type: System.Int32, IsInvalid) (Syntax: 'x') - Instance Receiver: + Instance Receiver: null - Right: + Right: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsInvalid) (Syntax: 'Prop = 1') - Left: + Left: IPropertyReferenceOperation: System.Int32 MemberInitializerTest.Prop { get; set; } (Static) (OperationKind.PropertyReference, Type: System.Int32, IsInvalid) (Syntax: 'Prop') - Instance Receiver: + Instance Receiver: null - Right: + Right: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') "; var expectedDiagnostics = new DiagnosticDescription[] { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs index 937a9988ccf5d..c0c092edba74b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs @@ -8630,6 +8630,62 @@ static R F() Diagnostic(ErrorCode.ERR_EscapeVariable, "y").WithArguments("y").WithLocation(25, 20)); } + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/65353")] + public void Local_NestedLambda([CombinatorialRange(0, 3)] int nesting) + { + var source = $$""" + {{new string('{', nesting)}} + int x = 1; + F(() => + { + int y = 2; + ref int r = ref y; + r = ref x; + }); + {{new string('}', nesting)}} + + static void F(System.Action a) { } + """; + CreateCompilation(source).VerifyDiagnostics(); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/65353")] + public void Local_NestedLocalFunction([CombinatorialRange(0, 3)] int nesting) + { + var source = $$""" + {{new string('{', nesting)}} + int x = 1; + F(); + void F() + { + int y = 2; + ref int r = ref y; + r = ref x; + } + {{new string('}', nesting)}} + """; + CreateCompilation(source).VerifyDiagnostics(); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/65353")] + public void Local_NestedLocalFunction_Parameter([CombinatorialRange(0, 3)] int nesting) + { + var source = $$""" + {{new string('{', nesting)}} + int x = 1; + F(ref x); + void F(ref int z) + { + z = ref x; + } + {{new string('}', nesting)}} + """; + CreateCompilation(source).VerifyDiagnostics( + // (6,9): error CS8374: Cannot ref-assign 'x' to 'z' because 'x' has a narrower escape scope than 'z'. + // z = ref x; + Diagnostic(ErrorCode.ERR_RefAssignNarrower, "z = ref x").WithArguments("z", "x").WithLocation(6, 9)); + } + [ConditionalFact(typeof(CoreClrOnly))] public void ParameterEscape() { @@ -10170,5 +10226,137 @@ public void Utf8Addition() """; CreateCompilation(code, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75592")] + public void SelfAssignment_ReturnOnly() + { + var source = """ + S s = default; + S.M(ref s); + + ref struct S + { + int field; + ref int refField; + + public static void M(ref S s) + { + s.refField = ref s.field; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (11,9): error CS9079: Cannot ref-assign 's.field' to 'refField' because 's.field' can only escape the current method through a return statement. + // s.refField = ref s.field; + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "s.refField = ref s.field").WithArguments("refField", "s.field").WithLocation(11, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75592")] + public void SelfAssignment_CallerContext() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + S s = default; + S.M(ref s); + + ref struct S + { + int field; + ref int refField; + + public static void M([UnscopedRef] ref S s) + { + s.refField = ref s.field; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (4,1): error CS8350: This combination of arguments to 'S.M(ref S)' is disallowed because it may expose variables referenced by parameter 's' outside of their declaration scope + // S.M(ref s); + Diagnostic(ErrorCode.ERR_CallArgMixing, "S.M(ref s)").WithArguments("S.M(ref S)", "s").WithLocation(4, 1), + // (4,9): error CS8168: Cannot return local 's' by reference because it is not a ref local + // S.M(ref s); + Diagnostic(ErrorCode.ERR_RefReturnLocal, "s").WithArguments("s").WithLocation(4, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75592")] + public void SelfAssignment_CallerContext_StructReceiver() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + S s = default; + s.M(); + + ref struct S + { + int field; + ref int refField; + + [UnscopedRef] public void M() + { + this.refField = ref this.field; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (13,9): error CS9079: Cannot ref-assign 'this.field' to 'refField' because 'this.field' can only escape the current method through a return statement. + // this.refField = ref this.field; + Diagnostic(ErrorCode.ERR_RefAssignReturnOnly, "this.refField = ref this.field").WithArguments("refField", "this.field").WithLocation(13, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75592")] + public void SelfAssignment_CallerContext_Out() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + S s = default; + S.M(out s); + + ref struct S + { + int field; + ref int refField; + + public static void M([UnscopedRef] out S s) + { + s = default; + s.refField = ref s.field; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (4,1): error CS8350: This combination of arguments to 'S.M(out S)' is disallowed because it may expose variables referenced by parameter 's' outside of their declaration scope + // S.M(out s); + Diagnostic(ErrorCode.ERR_CallArgMixing, "S.M(out s)").WithArguments("S.M(out S)", "s").WithLocation(4, 1), + // (4,9): error CS8168: Cannot return local 's' by reference because it is not a ref local + // S.M(out s); + Diagnostic(ErrorCode.ERR_RefReturnLocal, "s").WithArguments("s").WithLocation(4, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75592")] + public void SelfAssignment_CallerContext_Scoped() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + scoped S s = default; + S.M(ref s); + + ref struct S + { + int field; + ref int refField; + + public static void M([UnscopedRef] ref S s) + { + s.refField = ref s.field; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 762d9f656e705..5e094ed174443 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -9757,6 +9757,12 @@ static ref S F2(S x2) else { comp.VerifyEmitDiagnostics( + // (14,26): error CS8350: This combination of arguments to 'Program.F1(ref S)' is disallowed because it may expose variables referenced by parameter 'x1' outside of their declaration scope + // ref var y2 = ref F1(ref x2); + Diagnostic(ErrorCode.ERR_CallArgMixing, "F1(ref x2)").WithArguments("Program.F1(ref S)", "x1").WithLocation(14, 26), + // (14,33): error CS8166: Cannot return a parameter by reference 'x2' because it is not a ref parameter + // ref var y2 = ref F1(ref x2); + Diagnostic(ErrorCode.ERR_RefReturnParameter, "x2").WithArguments("x2").WithLocation(14, 33), // (15,20): error CS8157: Cannot return 'y2' by reference because it was initialized to a value that cannot be returned by reference // return ref y2; // 1 Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "y2").WithArguments("y2").WithLocation(15, 20)); @@ -29523,9 +29529,15 @@ private ref struct RefStruct // (9,9): error CS8374: Cannot ref-assign 'value' to 'RefField' because 'value' has a narrower escape scope than 'RefField'. // GetReference2(in s1).RefField = ref value; // 2 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "GetReference2(in s1).RefField = ref value").WithArguments("RefField", "value").WithLocation(9, 9), + // (10,9): error CS8350: This combination of arguments to 'Repro.GetReference3(out Repro.RefStruct)' is disallowed because it may expose variables referenced by parameter 'rs' outside of their declaration scope + // GetReference3(out s1).RefField = ref value; // 3 + Diagnostic(ErrorCode.ERR_CallArgMixing, "GetReference3(out s1)").WithArguments("Repro.GetReference3(out Repro.RefStruct)", "rs").WithLocation(10, 9), // (10,9): error CS8374: Cannot ref-assign 'value' to 'RefField' because 'value' has a narrower escape scope than 'RefField'. // GetReference3(out s1).RefField = ref value; // 3 Diagnostic(ErrorCode.ERR_RefAssignNarrower, "GetReference3(out s1).RefField = ref value").WithArguments("RefField", "value").WithLocation(10, 9), + // (10,27): error CS8168: Cannot return local 's1' by reference because it is not a ref local + // GetReference3(out s1).RefField = ref value; // 3 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "s1").WithArguments("s1").WithLocation(10, 27), // (12,9): error CS8332: Cannot assign to a member of method 'GetReadonlyReference1' or use it as the right hand side of a ref assignment because it is a readonly variable // GetReadonlyReference1(ref s1).RefField = ref value; // 4 Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "GetReadonlyReference1(ref s1).RefField").WithArguments("method", "GetReadonlyReference1").WithLocation(12, 9), @@ -29534,7 +29546,13 @@ private ref struct RefStruct Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "GetReadonlyReference2(in s1).RefField").WithArguments("method", "GetReadonlyReference2").WithLocation(13, 9), // (14,9): error CS8332: Cannot assign to a member of method 'GetReadonlyReference3' or use it as the right hand side of a ref assignment because it is a readonly variable // GetReadonlyReference3(out s1).RefField = ref value; // 6 - Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "GetReadonlyReference3(out s1).RefField").WithArguments("method", "GetReadonlyReference3").WithLocation(14, 9)); + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "GetReadonlyReference3(out s1).RefField").WithArguments("method", "GetReadonlyReference3").WithLocation(14, 9), + // (14,9): error CS8350: This combination of arguments to 'Repro.GetReadonlyReference3(out Repro.RefStruct)' is disallowed because it may expose variables referenced by parameter 'rs' outside of their declaration scope + // GetReadonlyReference3(out s1).RefField = ref value; // 6 + Diagnostic(ErrorCode.ERR_CallArgMixing, "GetReadonlyReference3(out s1)").WithArguments("Repro.GetReadonlyReference3(out Repro.RefStruct)", "rs").WithLocation(14, 9), + // (14,35): error CS8168: Cannot return local 's1' by reference because it is not a ref local + // GetReadonlyReference3(out s1).RefField = ref value; // 6 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "s1").WithArguments("s1").WithLocation(14, 35)); } [WorkItem(66128, "https://github.com/dotnet/roslyn/issues/66128")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index a8697628f5712..48e81057165b6 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics { - [CompilerTrait(CompilerFeature.RefLocalsReturns)] + [CompilerTrait(CompilerFeature.RefLocalsReturns, CompilerFeature.RefLifetime)] public class RefLocalsAndReturnsTests : CompilingTestBase { [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs index 1c536714a4cf7..bffc78b186f0e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs @@ -16515,10 +16515,7 @@ static void Main() Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(11, 34), // (11,34): error CS1002: ; expected // Console.WriteLine("); - Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(11, 34), - // (9,13): error CS1688: Cannot convert anonymous method block without a parameter list to delegate type 'OutParam' because it has one or more out parameters - // o = delegate // CS1688 - Diagnostic(ErrorCode.ERR_CantConvAnonMethNoParams, "delegate").WithArguments("OutParam").WithLocation(9, 13)); + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(11, 34)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/TypeOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TypeOfTests.cs index 61ee254c26203..fbb75ecdb1d65 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/TypeOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TypeOfTests.cs @@ -37,7 +37,91 @@ public C(int i) Assert.Equal("C..ctor(System.Int32 i)", symbolInfo.Symbol.ToTestDisplayString()); var typeInfo = model.GetTypeInfo(node); Assert.Equal("C", typeInfo.Type.ToTestDisplayString()); + } + + [Fact] + public void TypeofPointer() + { + CreateCompilation(""" + class C + { + unsafe void M() + { + var v = typeof(int*); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void TypeofFunctionPointer1() + { + CreateCompilation(""" + class C + { + unsafe void M() + { + var v = typeof(delegate*); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void TypeofFunctionPointer2() + { + CreateCompilation(""" + using System.Collections.Generic; + + class C + { + unsafe void M() + { + var v = typeof(delegate*,int>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void TypeofFunctionPointer3() + { + CreateCompilation(""" + using System.Collections.Generic; + + class C + { + unsafe void M() + { + var v = typeof(delegate*,int>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,34): error CS7003: Unexpected use of an unbound generic name + // var v = typeof(delegate*,int>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(7, 34)); + } + + [Fact] + public void TypeofFunctionPointer4() + { + CreateCompilation(""" + using System.Collections.Generic; + class D + { + unsafe void M() + { + var v = typeof(D<, delegate*, List<>>); + } + } + """, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (7,26): error CS1031: Type expected + // var v = typeof(D<, delegate*, List<>>); + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(7, 26), + // (7,44): error CS7003: Unexpected use of an unbound generic name + // var v = typeof(D<, delegate*, List<>>); + Diagnostic(ErrorCode.ERR_UnexpectedUnboundGenericName, "List<>").WithLocation(7, 44)); } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UseSiteErrorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UseSiteErrorTests.cs index 172b3255fee4b..5bb1106bc6dc9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UseSiteErrorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UseSiteErrorTests.cs @@ -2728,6 +2728,8 @@ async Task M1() "; var comp = CreateCompilation(source, references: new[] { UnmanagedUseSiteError_Ref2 }); comp.VerifyEmitDiagnostics( + // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1), // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1), // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs index 9c568f9d76dcb..9bc25adb50ba3 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs @@ -4745,6 +4745,24 @@ class C(out Type[M2(out object y)]) Assert.Null(model.GetAliasInfo(identifier)); } + [Fact] + public void CommonPreprocessingSymbolProperties() + { + var text = """ + #if NET5_0_OR_GREATER + #endif + """; + var compilation = CreateCompilation(text); + + var tree = compilation.SyntaxTrees.Single(); + var identifier = tree.GetRoot().DescendantNodes(descendIntoTrivia: true).OfType().First(); + var model = compilation.GetSemanticModel(tree); + var preprocessingSymbol = model.GetPreprocessingSymbolInfo(identifier).Symbol; + Assert.NotNull(preprocessingSymbol); + Assert.Equal("NET5_0_OR_GREATER", preprocessingSymbol.Name); + Assert.True(preprocessingSymbol.CanBeReferencedByName); + } + #region "regression helper" private void Regression(string text) { diff --git a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs index 8b6b16d4ef626..add91eb5d5684 100644 --- a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs @@ -1448,7 +1448,7 @@ class C { var format = new SymbolDisplayFormat( memberOptions: SymbolDisplayMemberOptions.IncludeType, - compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames); + compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames); TestSymbolDescription( text, @@ -2049,8 +2049,10 @@ class C SymbolDisplayPartKind.Punctuation); } - [Fact] - public void TestPropertyGetAccessor() + [Theory] + [InlineData(SymbolDisplayCompilerInternalOptions.None)] + [InlineData(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames)] + internal void TestPropertyGetAccessor(SymbolDisplayCompilerInternalOptions internalOptions) { var text = @" class C { @@ -2062,6 +2064,7 @@ class C { GetMembers("get_P").Single(); var format = new SymbolDisplayFormat( + internalOptions, memberOptions: SymbolDisplayMemberOptions.IncludeAccessibility | SymbolDisplayMemberOptions.IncludeContainingType | @@ -2123,6 +2126,175 @@ class C { SymbolDisplayPartKind.Keyword); } + [Fact] + public void TestPropertyBackingField() + { + var text = @" +#nullable enable +class C { + string P { get; set; } } +"; + + Func findSymbol = global => + global.GetTypeMembers("C", 0).Single(). + GetMembers("

k__BackingField").Single(); + + var format = new SymbolDisplayFormat( + memberOptions: + SymbolDisplayMemberOptions.IncludeAccessibility | + SymbolDisplayMemberOptions.IncludeContainingType | + SymbolDisplayMemberOptions.IncludeExplicitInterface | + SymbolDisplayMemberOptions.IncludeModifiers | + SymbolDisplayMemberOptions.IncludeParameters | + SymbolDisplayMemberOptions.IncludeType); + + TestSymbolDescription( + text, + findSymbol, + format, + "private String C.P.field", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.ClassName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.ClassName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + } + + [Fact] + public void TestPropertyBackingFieldFromCompilationReference() + { + var text = @" +#nullable enable +class C { + string P { get; set; } } +"; + + var format = new SymbolDisplayFormat( + memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeModifiers | SymbolDisplayMemberOptions.IncludeAccessibility | SymbolDisplayMemberOptions.IncludeType, + parameterOptions: SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeDefaultValue, + miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + var comp1 = CreateCompilation(text); + var comp2 = CreateCompilation("", references: [comp1.ToMetadataReference()], options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + + var prop = comp2.GetMember("C.

k__BackingField").GetPublicSymbol(); + var parts = SymbolDisplay.ToDisplayParts(prop, format); + + Verify( + parts, + "private string P.field", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + } + + [Fact] + public void TestPropertyBackingField_UseMetadataMethodNames() + { + var text = @" +#nullable enable +class C { + string P { get; set; } } +"; + + Func findSymbol = global => + global.GetTypeMembers("C", 0).Single(). + GetMembers("

k__BackingField").Single(); + + var format = new SymbolDisplayFormat( + compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames, + memberOptions: + SymbolDisplayMemberOptions.IncludeAccessibility | + SymbolDisplayMemberOptions.IncludeContainingType | + SymbolDisplayMemberOptions.IncludeExplicitInterface | + SymbolDisplayMemberOptions.IncludeModifiers | + SymbolDisplayMemberOptions.IncludeParameters | + SymbolDisplayMemberOptions.IncludeType); + + TestSymbolDescription( + text, + findSymbol, + format, + "private String C.

k__BackingField", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.ClassName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.ClassName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.FieldName); + } + + [Theory] + [InlineData(SymbolDisplayCompilerInternalOptions.None)] + [InlineData(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames)] + internal void TestPropertyBackingFieldFromMetadata(SymbolDisplayCompilerInternalOptions internalOptions) + { + // Metadata symbols do not associate the backing field with the property, so the metadata name is always displayed. + var text = @" +#nullable enable +class C { + string P { get; set; } } +"; + + var format = new SymbolDisplayFormat( + compilerInternalOptions: internalOptions, + memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeModifiers | SymbolDisplayMemberOptions.IncludeAccessibility | SymbolDisplayMemberOptions.IncludeType, + parameterOptions: SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeDefaultValue, + miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + var comp1 = CreateCompilation(text); + var comp2 = CreateCompilation("", references: [comp1.EmitToImageReference()], options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + + var prop = comp2.GetMember("C.

k__BackingField").GetPublicSymbol(); + var parts = SymbolDisplay.ToDisplayParts(prop, format); + + Verify( + parts, + "private string

k__BackingField", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.FieldName); + } + + [Fact] + public void TestPropertyBackingFieldVB() + { + var text = @" +Class A + Public Property Prop As String +End Class"; + + var format = new SymbolDisplayFormat( + memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeModifiers | SymbolDisplayMemberOptions.IncludeAccessibility | SymbolDisplayMemberOptions.IncludeType, + parameterOptions: SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeDefaultValue, + miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + var comp = CreateVisualBasicCompilation("c", text); + var a = (ITypeSymbol)comp.GlobalNamespace.GetMembers("A").Single(); + var goo = a.GetMembers("_Prop").Single(); + var parts = SymbolDisplay.ToDisplayParts(goo, format); + + Verify( + parts, + "private string _Prop", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.FieldName); + } + [Fact] public void TestMemberEventAll() { @@ -8837,5 +9009,37 @@ void M() SymbolDisplayPartKind.ParameterName, SymbolDisplayPartKind.Punctuation); } + + [Fact, WorkItem(66009, "https://github.com/dotnet/roslyn/issues/66009")] + public void PreprocessingSymbol() + { + var source = """ + #if NET5_0_OR_GREATER + #endif + """; + + var comp = CreateCompilation(source); + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var preprocessingNameSyntax = tree.GetRoot().DescendantNodes(descendIntoTrivia: true) + .OfType().First(); + var preprocessingSymbolInfo = model.GetPreprocessingSymbolInfo(preprocessingNameSyntax); + var preprocessingSymbol = preprocessingSymbolInfo.Symbol; + + var format = new SymbolDisplayFormat( + memberOptions: SymbolDisplayMemberOptions.IncludeParameters, + parameterOptions: SymbolDisplayParameterOptions.IncludeType | + SymbolDisplayParameterOptions.IncludeName | + SymbolDisplayParameterOptions.IncludeDefaultValue); + + Assert.Equal("NET5_0_OR_GREATER", preprocessingSymbol.ToDisplayString(format)); + + var displayParts = preprocessingSymbol.ToDisplayParts(format); + AssertEx.Equal( + expected: [ + new SymbolDisplayPart(SymbolDisplayPartKind.Text, preprocessingSymbol, "NET5_0_OR_GREATER") + ], + actual: displayParts); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs index c461c0a0129c9..ee425b79b53d3 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs @@ -13,6 +13,7 @@ using Roslyn.Test.Utilities; using Xunit; using Basic.Reference.Assemblies; +using Microsoft.CodeAnalysis.Test.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols { @@ -425,6 +426,346 @@ static void Main() Assert.True(conversion.IsNumeric); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36377")] + public void GetSymbolInfo_ExplicitCastOnMethodGroup() + { + var src = """ +public class C +{ + public static void M() + { + C x = (C)C.Test; + } + + public static int Test() => 1; + + public static explicit operator C(System.Func intDelegate) + { + return new C(); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Equal("System.Int32 C.Test()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75833")] + public void GetSymbolInfo_ImplicitUserDefinedConversionOnMethodGroup_InLocalDeclaration() + { + var src = """ +public class C +{ + public static void M() + { + C x = C.Test; + } + + public static int Test() => 1; + + public static implicit operator C(System.Func intDelegate) + { + return new C(); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,17): error CS0428: Cannot convert method group 'Test' to non-delegate type 'C'. Did you intend to invoke the method? + // C x = C.Test; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "Test").WithArguments("Test", "C").WithLocation(5, 17)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Equal("C C.op_Implicit(System.Func intDelegate)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); // Unexpected: Should be null + var conversion = model.GetConversion(memberAccess); + Assert.Equal(ConversionKind.ExplicitUserDefined, conversion.Kind); // Unexpected: Should be NoConversion or possibly Identity for error case + Assert.Equal(ConversionKind.MethodGroup, conversion.UserDefinedFromConversion.Kind); + Assert.Equal(ConversionKind.Identity, conversion.UserDefinedToConversion.Kind); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75833")] + public void GetSymbolInfo_ImplicitUserDefinedConversionOnMethodGroup_WithToConversion_InLocalDeclaration() + { + var src = """ +public struct C +{ + public static void M() + { + C? x = C.Test; + } + + public static int Test() => 1; + + public static implicit operator C(System.Func intDelegate) + { + return new C(); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,18): error CS0428: Cannot convert method group 'Test' to non-delegate type 'C?'. Did you intend to invoke the method? + // C? x = C.Test; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "Test").WithArguments("Test", "C?").WithLocation(5, 18)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + var conversion = model.GetConversion(memberAccess); + Assert.Equal(ConversionKind.ExplicitUserDefined, conversion.Kind); // Unexpected: Should be NoConversion or possibly Identity for error case + Assert.Equal(ConversionKind.MethodGroup, conversion.UserDefinedFromConversion.Kind); + Assert.Equal(ConversionKind.Identity, conversion.UserDefinedToConversion.Kind); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75833")] + public void GetSymbolInfo_ImplicitUserDefinedConversionOnMethodGroup_InAssignemnt() + { + var src = """ +public class C +{ + public static void M() + { + C x; + x = C.Test; + } + + public static int Test() => 1; + + public static implicit operator C(System.Func intDelegate) + { + return new C(); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (6,15): error CS0428: Cannot convert method group 'Test' to non-delegate type 'C'. Did you intend to invoke the method? + // x = C.Test; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "Test").WithArguments("Test", "C").WithLocation(6, 15)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Equal("C C.op_Implicit(System.Func intDelegate)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); // Unexpected: Should be null + var conversion = model.GetConversion(memberAccess); + Assert.Equal(ConversionKind.ExplicitUserDefined, conversion.Kind); // Unexpected: Should be NoConversion or possibly Identity for error case + Assert.Equal(ConversionKind.MethodGroup, conversion.UserDefinedFromConversion.Kind); + Assert.Equal(ConversionKind.Identity, conversion.UserDefinedToConversion.Kind); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75833")] + public void GetSymbolInfo_ImplicitUserDefinedConversionOnMethodGroup_WithToConversion_InAssignment() + { + var src = """ +public struct C +{ + public static void M() + { + C? x; + x = C.Test; + } + + public static int Test() => 1; + + public static implicit operator C(System.Func intDelegate) + { + return new C(); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (6,15): error CS0428: Cannot convert method group 'Test' to non-delegate type 'C?'. Did you intend to invoke the method? + // x = C.Test; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "Test").WithArguments("Test", "C?").WithLocation(6, 15)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); // Unexpected: Should be null + var conversion = model.GetConversion(memberAccess); + Assert.Equal(ConversionKind.ExplicitUserDefined, conversion.Kind); // Unexpected: Should be NoConversion or possibly Identity for error case + Assert.Equal(ConversionKind.MethodGroup, conversion.UserDefinedFromConversion.Kind); + Assert.Equal(ConversionKind.Identity, conversion.UserDefinedToConversion.Kind); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75833")] + public void GetSymbolInfo_ImplicitUserDefinedConversionOnMethodGroup_InInvocationArgument() + { + var src = """ +public class C +{ + public static void M() + { + M2(C.Test); + } + + public static void M2(C c) { } + + public static int Test() => 1; + + public static implicit operator C(System.Func intDelegate) => throw null; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,12): error CS1503: Argument 1: cannot convert from 'method group' to 'C' + // M2(C.Test); + Diagnostic(ErrorCode.ERR_BadArgType, "C.Test").WithArguments("1", "method group", "C").WithLocation(5, 12)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + var conversion = model.GetConversion(memberAccess); + Assert.Equal(ConversionKind.Identity, conversion.Kind); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75833")] + public void GetSymbolInfo_ImplicitUserDefinedConversionOnMethodGroup_WithToConversion_InInvocationArgument() + { + var src = """ +public struct C +{ + public static void M() + { + M2(C.Test); + } + + public static void M2(C? c) { } + + public static int Test() => 1; + + public static implicit operator C(System.Func intDelegate) + { + return new C(); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,12): error CS1503: Argument 1: cannot convert from 'method group' to 'C?' + // M2(C.Test); + Diagnostic(ErrorCode.ERR_BadArgType, "C.Test").WithArguments("1", "method group", "C?").WithLocation(5, 12)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + var conversion = model.GetConversion(memberAccess); + Assert.Equal(ConversionKind.Identity, conversion.Kind); + } + + [Fact] + public void GetSymbolInfo_MethodGroupConversionInLocalDeclaration() + { + var src = """ +public class C +{ + public static void M() + { + System.Func x = C.Test; + } + + public static int Test() => 1; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Equal("System.Int32 C.Test()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + var conversion = model.GetConversion(memberAccess); + Assert.Equal(ConversionKind.MethodGroup, conversion.Kind); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36377")] + public void GetSymbolInfo_TwoExplicitCastsOnMethodGroup() + { + var src = """ +public sealed class C +{ + public static void M() + { + D x = (D)(C)C.Test; + } + + public static int Test() => 1; + + public static explicit operator C(System.Func intDelegate) => throw null; +} +public sealed class D +{ + public static explicit operator D(C c) => throw null; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Equal("System.Int32 C.Test()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36377")] + public void GetSymbolInfo_NoConversion() + { + var src = """ +public sealed class C +{ + public static void M() + { + int x = C.Test; + } + + public static int Test() => 1; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,19): error CS0428: Cannot convert method group 'Test' to non-delegate type 'int'. Did you intend to invoke the method? + // int x = C.Test; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "Test").WithArguments("Test", "int").WithLocation(5, 19)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36377")] + public void GetSymbolInfo_MethodGroupConversion() + { + var src = """ +public sealed class C +{ + public static void M() + { + System.Func x = C.Test; + } + + public static int Test() => 1; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Equal("System.Int32 C.Test()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + #region "Diagnostics" [Fact] public void VarianceRelationFail() diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs index e900435541515..28b8ca9a92740 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs @@ -51,25 +51,13 @@ private static Verification VerifyOnMonoOrCoreClr private static Verification Verify(bool isStatic) { + // IL Verify complains about static constrained calls: "Missing callvirt following constrained prefix." return isStatic ? Verification.Skipped : VerifyOnMonoOrCoreClr; } private static bool Execute(bool isStatic, bool haveImplementationInDerivedInterface = false, bool hasImplementationOfVirtualInDerivedType = false) { - // The runtime ignores the implementation of a static virtual method in derived types - // Tracked by https://github.com/dotnet/roslyn/issues/64501 - if (isStatic && hasImplementationOfVirtualInDerivedType) - { - return false; - } - - // https://github.com/dotnet/roslyn/issues/61321 : Enable execution for isStatic and haveImplementationInDerivedInterface once runtime can handle it. - if (!ExecutionConditionUtil.IsMonoOrCoreClr || (isStatic && haveImplementationInDerivedInterface)) - { - return false; - } - - return true; + return ExecutionConditionUtil.IsMonoOrCoreClr; } private static Verification VerifyOnMonoOrCoreClr_FailsIlVerify diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs index 204624bfe5c6c..fc44f296c1a3d 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; @@ -423,5 +424,105 @@ static string MyExtension(this object o) } """).VerifyEmitDiagnostics(); } + + [Fact] + public void ContainingSymbol_LocalFunction() + { + var source = """ +public class C +{ + public void M() + { + local(); + void local() { } + } +} +"""; + var comp = CreateCompilation([source]); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "local()"); + var symbol = model.GetSymbolInfo(invocation).Symbol; + Assert.Equal("void local()", symbol.ToTestDisplayString()); + Assert.Equal("void C.M()", symbol.ContainingSymbol.ToTestDisplayString()); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75543")] + public void ContainingSymbol_ConstructedLocalFunction() + { + var source = """ +public class C +{ + public void M() + { + local(new C()); + void local(T t) { } + } +} +"""; + var comp = CreateCompilation([source]); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "local(new C())"); + var symbol = model.GetSymbolInfo(invocation).Symbol; + Assert.Equal("void local(C t)", symbol.ToTestDisplayString()); + Assert.Equal("void C.M()", symbol.ContainingSymbol.ToTestDisplayString()); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75543")] + public void ContainingSymbol_ConstructedLocalFunction_Nested() + { + var source = """ +public class C +{ + public void M() + { + outer(); + + void outer() + { + local(42); + void local(T4 t) { } + } + } +} +"""; + var comp = CreateCompilation([source]); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "local(42)"); + var symbol = model.GetSymbolInfo(invocation).Symbol; + Assert.Equal("void local(System.Int32 t)", symbol.ToTestDisplayString()); + Assert.Equal("void outer()", symbol.ContainingSymbol.ToTestDisplayString()); + Assert.Equal("void C.M()", symbol.ContainingSymbol.ContainingSymbol.ToTestDisplayString()); + } + + [Fact] + public void ContainingSymbol_ConstructedMethod() + { + var source = """ +C.M(); + +public class C +{ + public static void M() { } +} +"""; + var comp = CreateCompilation([source]); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "C.M()"); + var symbol = model.GetSymbolInfo(invocation).Symbol; + Assert.Equal("void C.M()", symbol.ToTestDisplayString()); + Assert.Equal("C", symbol.ContainingSymbol.ToTestDisplayString()); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs index 63df7b20d52b8..4f68a2f3315f3 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs @@ -4517,17 +4517,19 @@ public C(bool unused) : this() ); } - [Fact, CompilerTrait(CompilerFeature.NullableReferenceTypes)] + [Theory, CompilerTrait(CompilerFeature.NullableReferenceTypes)] [WorkItem(6754, "https://github.com/dotnet/csharplang/issues/6754")] - public void RequiredMemberSuppressesNullabilityWarnings_MemberNotNull_ChainedConstructor_03() + [InlineData("public string Property2 { get; set; }")] + [InlineData("public string Property2 { get => field; set => field = value; }")] + public void RequiredMemberSuppressesNullabilityWarnings_MemberNotNull_ChainedConstructor_03(string property2Definition) { - var code = """ + var code = $$""" using System.Diagnostics.CodeAnalysis; #nullable enable class C { public required string Property1 { get => Property2; [MemberNotNull(nameof(Property2))] set => Property2 = value; } - public string Property2 { get; set; } + {{property2Definition}} public C() { } public C(bool unused) : this() @@ -4549,17 +4551,19 @@ public C(bool unused) : this() ); } - [Fact, CompilerTrait(CompilerFeature.NullableReferenceTypes)] + [Theory, CompilerTrait(CompilerFeature.NullableReferenceTypes)] [WorkItem(6754, "https://github.com/dotnet/csharplang/issues/6754")] - public void RequiredMemberSuppressesNullabilityWarnings_MemberNotNull_ChainedConstructor_04() + [InlineData("public string Property2 { get; set; }")] + [InlineData("public string Property2 { get => field; set => field = value; }")] + public void RequiredMemberSuppressesNullabilityWarnings_MemberNotNull_ChainedConstructor_04(string property2Definition) { - var code = """ + var code = $$""" using System.Diagnostics.CodeAnalysis; #nullable enable class C { public required string Property1 { get => Property2; [MemberNotNull(nameof(Property2))] set => Property2 = value; } - public string Property2 { get; set; } + {{property2Definition}} public C() { } [SetsRequiredMembers] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs index 603c82ac99b84..f824f4e8e58a1 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/RecordTests.cs @@ -1076,11 +1076,11 @@ record C AssertEx.Equal(new[] { "System.Type! C.EqualityContract.get", "System.Type! C.EqualityContract { get; }", - "System.Int32 C.k__BackingField", + "System.Int32 C.X.field", "System.Int32 C.X { get; init; }", "System.Int32 C.X.get", "void C.X.init", - "System.String! C.k__BackingField", + "System.String! C.Y.field", "System.String! C.Y { get; init; }", "System.String! C.Y.get", "void C.Y.init", diff --git a/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/PreprocessorTests.cs b/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/PreprocessorTests.cs index 152046ecb9111..939f4b350e8fe 100644 --- a/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/PreprocessorTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/PreprocessorTests.cs @@ -2426,7 +2426,7 @@ class A { } #define XXX var node = Parse(text); TestRoundTripping(node, text, false); VerifyErrorCode(node, (int)ErrorCode.ERR_BadDirectivePlacement); // CS1040 - VerifyDirectivesSpecial(node, new DirectiveInfo { Kind = SyntaxKind.DefineDirectiveTrivia, Status = NodeStatus.IsActive, Text = "XXX" }); + VerifyDirectivesSpecial(node); } [Fact] @@ -2587,7 +2587,7 @@ class A { } #undef XXX var node = Parse(text); TestRoundTripping(node, text, false); VerifyErrorCode(node, (int)ErrorCode.ERR_BadDirectivePlacement); - VerifyDirectivesSpecial(node, new DirectiveInfo { Kind = SyntaxKind.UndefDirectiveTrivia, Status = NodeStatus.IsActive, Text = "XXX" }); + VerifyDirectivesSpecial(node); } [Fact] diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ForStatementParsingTest.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ForStatementParsingTest.cs new file mode 100644 index 0000000000000..75dcb989303d4 --- /dev/null +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ForStatementParsingTest.cs @@ -0,0 +1,451 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Roslyn.Test.Utilities; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Parsing; + +public sealed class ForStatementParsingTest(ITestOutputHelper output) : ParsingTests(output) +{ + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66522")] + public void TestCommaSeparators1() + { + UsingStatement("for (int i = 0, j = 0; i < 10; i++) ;"); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "j"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "10"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66522")] + public void TestCommaSeparators2() + { + UsingStatement("for (int i = 0, i < 10; i++) ;", + // (1,15): error CS1002: ; expected + // for (int i = 0, i < 10; i++) ; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(1, 15)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + } + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "10"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66522")] + public void TestCommaSeparators3() + { + UsingStatement("for (int i = 0, i < 10, i++) ;", + // (1,15): error CS1002: ; expected + // for (int i = 0, i < 10, i++) ; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(1, 15), + // (1,23): error CS1002: ; expected + // for (int i = 0, i < 10, i++) ; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(1, 23)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + } + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "10"); + } + } + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66522")] + public void TestCommaSeparators4() + { + UsingStatement("for (int i = 0, i) ;", + // (1,18): error CS1002: ; expected + // for (int i = 0, i) ; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 18), + // (1,18): error CS1525: Invalid expression term ')' + // for (int i = 0, i) ; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(1, 18), + // (1,18): error CS1002: ; expected + // for (int i = 0, i) ; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(1, 18)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + } + } + M(SyntaxKind.SemicolonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66522")] + public void TestCommaSeparators5() + { + UsingStatement("for (int i = 0,,) ;", + // (1,15): error CS1002: ; expected + // for (int i = 0,,) ; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(1, 15), + // (1,16): error CS1002: ; expected + // for (int i = 0,,) ; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(1, 16)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + } + M(SyntaxKind.SemicolonToken); + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66522")] + public void TestCommaSeparators6() + { + UsingStatement("for (int i = 0, j; i < 10; i++) ;"); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "j"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "10"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } + + [Fact] + public void TestVariableDeclaratorVersusCondition1() + { + UsingStatement("for (int i = 0, i++; i < 10; i++) ;", + // (1,15): error CS1002: ; expected + // for (int i = 0, i++; i < 10; i++) ; + Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(1, 15), + // (1,28): error CS1003: Syntax error, ',' expected + // for (int i = 0, i++; i < 10; i++) ; + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(1, 28)); + + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + } + M(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "10"); + } + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.PlusPlusToken); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.EmptyStatement); + { + N(SyntaxKind.SemicolonToken); + } + } + EOF(); + } +} diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingErrorRecoveryTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingErrorRecoveryTests.cs index 268b149eab6d4..608a21ab96e73 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingErrorRecoveryTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingErrorRecoveryTests.cs @@ -8110,5 +8110,577 @@ public void RazorCommentRecovery_NoStart() } EOF(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_01() + { + UsingTree(""" + if (#if) + """, + // (1,5): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // if (#if) + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 5), + // (1,9): error CS1733: Expected expression + // if (#if) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 9), + // (1,9): error CS1026: ) expected + // if (#if) + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(1, 9), + // (1,9): error CS1733: Expected expression + // if (#if) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 9), + // (1,9): error CS1002: ; expected + // if (#if) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 9)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_01_WhitespaceBeforeHash() + { + UsingTree(""" + if ( #if) + """, + // (1,5): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // if ( #if) + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 6), + // (1,9): error CS1733: Expected expression + // if ( #if) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 10), + // (1,9): error CS1026: ) expected + // if ( #if) + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(1, 10), + // (1,9): error CS1733: Expected expression + // if ( #if) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 10), + // (1,9): error CS1002: ; expected + // if ( #if) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 10)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_01_WhitespaceAfterHash() + { + UsingTree(""" + if ( # if) + """, + // (1,5): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // if ( # if) + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 6), + // (1,9): error CS1733: Expected expression + // if ( # if) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 11), + // (1,9): error CS1026: ) expected + // if ( # if) + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(1, 11), + // (1,9): error CS1733: Expected expression + // if ( # if) + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 11), + // (1,9): error CS1002: ; expected + // if ( # if) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 11)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_02() + { + UsingTree(""" + if (#if false + x + #else + y + #endif + """, + // (1,5): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // if (#if false + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 5), + // (2,2): error CS1026: ) expected + // x + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(2, 2), + // (3,1): error CS1028: Unexpected preprocessor directive + // #else + Diagnostic(ErrorCode.ERR_UnexpectedDirective, "#else").WithLocation(3, 1), + // (4,2): error CS1002: ; expected + // y + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 2), + // (5,1): error CS1028: Unexpected preprocessor directive + // #endif + Diagnostic(ErrorCode.ERR_UnexpectedDirective, "#endif").WithLocation(5, 1)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + M(SyntaxKind.CloseParenToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + M(SyntaxKind.SemicolonToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_03() + { + UsingTree(""" + a(); + #if false + b(); + /* comment */ #else + c(); + #endif + """); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_04() + { + UsingTree(""" + a(); + #if true + b(); + /* comment */ #elif false + c(); + #endif + """, + // (4,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #elif false + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(4, 15)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_05() + { + UsingTree(""" + a(); + #if true + b(); + /* comment */ #endif + #else + c(); + #endif + d(); + """, + // (4,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #endif + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(4, 15)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "d"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_Define() + { + UsingTree(""" + /* comment */ #define ABC + #if ABC + x(); + #else + y(); + #endif + """, + // (1,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #define ABC + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 15)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_Undefine() + { + UsingTree(""" + #define ABC + /* comment */ #undefine ABC + #if ABC + x(); + #else + y(); + #endif + """, + // (2,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #undefine ABC + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(2, 15)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_ErrorWarning() + { + UsingTree(""" + /* comment */ #error E1 + /* comment */ #warning W1 + #error E2 + #warning W2 + """, + // (1,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #error E1 + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 15), + // (2,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #warning W1 + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(2, 15), + // (3,8): error CS1029: #error: 'E2' + // #error E2 + Diagnostic(ErrorCode.ERR_ErrorDirective, "E2").WithArguments("E2").WithLocation(3, 8), + // (4,10): warning CS1030: #warning: 'W2' + // #warning W2 + Diagnostic(ErrorCode.WRN_WarningDirective, "W2").WithArguments("W2").WithLocation(4, 10)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_Line() + { + UsingTree(""" + #line 200 + /* comment */ #line 100 + #error E1 + """, + // (200,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #line 100 + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(200, 15), + // (201,8): error CS1029: #error: 'E1' + // #error E1 + Diagnostic(ErrorCode.ERR_ErrorDirective, "E1").WithArguments("E1").WithLocation(201, 8)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_Pragma() + { + CreateCompilation(""" + #pragma warning disable 8321 + /* comment */ #pragma warning restore 8321 + void f() { } + #pragma warning restore 8321 + void g() { } + """).VerifyDiagnostics( + // (2,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #pragma warning restore 8321 + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(2, 15), + // (5,6): warning CS8321: The local function 'g' is declared but never used + // void g() { } + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "g").WithArguments("g").WithLocation(5, 6)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void PreprocessorDirective_Trailing_Nullable() + { + CreateCompilation(""" + #nullable disable + /* comment */ #nullable enable + _ = (object)null; + #nullable enable + _ = (object)null; // 1 + """).VerifyDiagnostics( + // (2,15): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // /* comment */ #nullable enable + Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(2, 15), + // (5,5): warning CS8600: Converting null literal or possible null value to non-nullable type. + // _ = (object)null; // 1 + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(object)null").WithLocation(5, 5)); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/TypeArgumentListParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/TypeArgumentListParsingTests.cs index 9a47468d3af2b..4a14eda2eeba3 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/TypeArgumentListParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/TypeArgumentListParsingTests.cs @@ -2,19 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public class TypeArgumentListParsingTests : ParsingTests + public sealed class TypeArgumentListParsingTests : ParsingTests { public TypeArgumentListParsingTests(ITestOutputHelper output) : base(output) { } - protected override SyntaxTree ParseTree(string text, CSharpParseOptions options) + protected override SyntaxTree ParseTree(string text, CSharpParseOptions? options) { return SyntaxFactory.ParseSyntaxTree(text, options: options); } @@ -2826,5 +2824,877 @@ void M() } EOF(); } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes1() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo.Instance; + } + } + """, + // (5,32): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 32), + // (5,33): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 33)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes2() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo.Instance; + } + } + """, + // (5,28): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 28), + // (5,29): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 29)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Id"); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes3() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo<,Id,>.Instance; + } + } + """, + // (5,25): error CS1031: Type expected + // var added = Goo<,Id,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 25), + // (5,29): error CS1031: Type expected + // var added = Goo<,Id,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 29)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Id"); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes4() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo<,,Id>.Instance; + } + } + """, + // (5,25): error CS1031: Type expected + // var added = Goo<,,Id>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 25), + // (5,26): error CS1031: Type expected + // var added = Goo<,,Id>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 26)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Id"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes5() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo.Instance; + } + } + """, + // (5,30): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 30), + // (5,31): error CS1031: Type expected + // var added = Goo.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 31)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Id"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes6() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo<(int i, int j),,>.Instance; + } + } + """, + // (5,40): error CS1031: Type expected + // var added = Goo<(int i, int j),,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 40), + // (5,41): error CS1031: Type expected + // var added = Goo<(int i, int j),,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 41)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "j"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes7() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo,,>.Instance; + } + } + """, + // (5,32): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 32), + // (5,33): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 33)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "K"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TestGenericWithExtraCommasAndMissingTypes8() + { + UsingTree(""" + class C + { + void M() + { + var added = Goo,,>.Instance; + } + } + """, + // (5,31): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 31), + // (5,32): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 32), + // (5,34): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ",").WithLocation(5, 34), + // (5,35): error CS1031: Type expected + // var added = Goo,,>.Instance; + Diagnostic(ErrorCode.ERR_TypeExpected, ">").WithLocation(5, 35)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "added"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Goo"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "K"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Instance"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNodeTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNodeTests.cs index 71967a077afe7..f9c4a90d8d350 100644 --- a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNodeTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNodeTests.cs @@ -347,24 +347,21 @@ public void TestGetAllDirectivesUsingDescendantNodes() public void TestContainsDirective() { // Empty compilation unit shouldn't have any directives in it. - for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.ScopedKeyword; kind++) + for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.XmlElement; kind++) Assert.False(SyntaxFactory.ParseCompilationUnit("").ContainsDirective(kind)); // basic file shouldn't have any directives in it. - for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.ScopedKeyword; kind++) + for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.XmlElement; kind++) Assert.False(SyntaxFactory.ParseCompilationUnit("namespace N { }").ContainsDirective(kind)); // directive in trailing trivia is not a thing - for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.ScopedKeyword; kind++) + for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.XmlElement; kind++) { var compilationUnit = SyntaxFactory.ParseCompilationUnit("namespace N { } #if false"); compilationUnit.GetDiagnostics().Verify( // (1,17): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line // namespace N { } #if false - TestBase.Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 17), - // (1,26): error CS1027: #endif directive expected - // namespace N { } #if false - TestBase.Diagnostic(ErrorCode.ERR_EndifDirectiveExpected, "").WithLocation(1, 26)); + TestBase.Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 17)); Assert.False(compilationUnit.ContainsDirective(kind)); } @@ -386,14 +383,14 @@ public void TestContainsDirective() testContainsHelper1("#undef x", SyntaxKind.UndefDirectiveTrivia); testContainsHelper1("#warning", SyntaxKind.WarningDirectiveTrivia); - // !# is special and is only recognized at start of a script file and nowhere else. + // #! is special and is only recognized at start of a script file and nowhere else. testContainsHelper2(new[] { SyntaxKind.ShebangDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit("#!command", options: TestOptions.Script)); testContainsHelper2(new[] { SyntaxKind.BadDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit(" #!command", options: TestOptions.Script)); testContainsHelper2(new[] { SyntaxKind.BadDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit("#!command", options: TestOptions.Regular)); return; - void testContainsHelper1(string directive, params SyntaxKind[] directiveKinds) + static void testContainsHelper1(string directive, params SyntaxKind[] directiveKinds) { Assert.True(directiveKinds.Length > 0); @@ -469,13 +466,13 @@ class D """)); } - void testContainsHelper2(SyntaxKind[] directiveKinds, CompilationUnitSyntax compilationUnit) + static void testContainsHelper2(SyntaxKind[] directiveKinds, CompilationUnitSyntax compilationUnit) { Assert.True(compilationUnit.ContainsDirectives); foreach (var directiveKind in directiveKinds) Assert.True(compilationUnit.ContainsDirective(directiveKind)); - for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.ScopedType; kind++) + for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.XmlElement; kind++) { if (!directiveKinds.Contains(kind)) Assert.False(compilationUnit.ContainsDirective(kind)); @@ -483,6 +480,32 @@ void testContainsHelper2(SyntaxKind[] directiveKinds, CompilationUnitSyntax comp } } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public void TestContainsDirective_IfIf() + { + var compilationUnit = SyntaxFactory.ParseCompilationUnit(""" + if (#if) + """); + compilationUnit.GetDiagnostics().Verify( + // (1,5): error CS1040: Preprocessor directives must appear as the first non-whitespace character on a line + // if (#if) + TestBase.Diagnostic(ErrorCode.ERR_BadDirectivePlacement, "#").WithLocation(1, 5), + // (1,9): error CS1733: Expected expression + // if (#if) + TestBase.Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 9), + // (1,9): error CS1026: ) expected + // if (#if) + TestBase.Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(1, 9), + // (1,9): error CS1733: Expected expression + // if (#if) + TestBase.Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 9), + // (1,9): error CS1002: ; expected + // if (#if) + TestBase.Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 9)); + Assert.False(compilationUnit.ContainsDirectives); + Assert.False(compilationUnit.ContainsDirective(SyntaxKind.IfDirectiveTrivia)); + } + [Fact] public void TestGetAllAnnotatedNodesUsingDescendantNodes() { @@ -2880,7 +2903,7 @@ public void TestRemoveBadDirectiveWithoutEOL_KeepEndOfLine_KeepDirectives() var text = cu2.ToFullString(); - Assert.Equal("class A { } \r\n#endregion", text); + Assert.Equal("class A { } ", text); } [Fact] @@ -3245,7 +3268,7 @@ class A { } #endregion"; var expectedText = @" #region A -#endregion"; +"; TestWithWindowsAndUnixEndOfLines(inputText, expectedText, (cu, expected) => { diff --git a/src/Compilers/Core/CodeAnalysisTest/Collections/List/SegmentedList.Generic.Tests.Capacity.cs b/src/Compilers/Core/CodeAnalysisTest/Collections/List/SegmentedList.Generic.Tests.Capacity.cs new file mode 100644 index 0000000000000..6df220121fb62 --- /dev/null +++ b/src/Compilers/Core/CodeAnalysisTest/Collections/List/SegmentedList.Generic.Tests.Capacity.cs @@ -0,0 +1,157 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis.Collections; +using Xunit; + +namespace Microsoft.CodeAnalysis.UnitTests.Collections +{ + ///

+ /// Contains tests that ensure the correctness of the List class. + /// + public abstract partial class SegmentedList_Generic_Tests : IList_Generic_Tests + where T : notnull + { + public static IEnumerable TestLengthsAndSegmentCounts + { + get + { + for (var segmentsToAdd = 1; segmentsToAdd < 4; segmentsToAdd++) + { + yield return new object[] { 1, segmentsToAdd }; + yield return new object[] { 10, segmentsToAdd }; + yield return new object[] { 100, segmentsToAdd }; + yield return new object[] { SegmentedArray.TestAccessor.SegmentSize / 2, segmentsToAdd }; + yield return new object[] { SegmentedArray.TestAccessor.SegmentSize, segmentsToAdd }; + yield return new object[] { SegmentedArray.TestAccessor.SegmentSize * 2, segmentsToAdd }; + yield return new object[] { 100000, segmentsToAdd }; + } + } + } + + [Theory] + [MemberData(nameof(ValidCollectionSizes))] + public void Capacity_ArgumentValidity(int initialCapacity) + { + var list = new SegmentedList(initialCapacity); + + for (var i = 0; i < initialCapacity; i++) + list.Add(CreateT(i)); + + Assert.Throws(() => list.Capacity = initialCapacity - 1); + } + + [Theory] + [InlineData(0, 1)] + [InlineData(0, 10)] + [InlineData(4, 6)] + [InlineData(4, 10)] + [InlineData(4, 100_000)] + public void Capacity_MatchesSizeRequested(int initialCapacity, int requestedCapacity) + { + var list = new SegmentedList(initialCapacity); + + list.Capacity = requestedCapacity; + + Assert.Equal(requestedCapacity, list.Capacity); + } + + [Theory] + [MemberData(nameof(TestLengthsAndSegmentCounts))] + public void Capacity_ReusesSegments(int initialCapacity, int segmentCountToAdd) + { + var elementCountToAdd = segmentCountToAdd * SegmentedArray.TestAccessor.SegmentSize; + + var segmented = new SegmentedList(initialCapacity); + + var oldSegments = SegmentedCollectionsMarshal.AsSegments(segmented.GetTestAccessor().Items); + var oldSegmentCount = oldSegments.Length; + + segmented.Capacity = initialCapacity + elementCountToAdd; + + var resizedSegments = SegmentedCollectionsMarshal.AsSegments(segmented.GetTestAccessor().Items); + var resizedSegmentCount = resizedSegments.Length; + + Assert.Equal(oldSegmentCount + segmentCountToAdd, resizedSegmentCount); + + for (var i = 0; i < oldSegmentCount - 1; i++) + Assert.Same(resizedSegments[i], oldSegments[i]); + + for (var i = oldSegmentCount - 1; i < resizedSegmentCount - 1; i++) + Assert.Equal(resizedSegments[i].Length, SegmentedArray.TestAccessor.SegmentSize); + + Assert.NotSame(resizedSegments[resizedSegmentCount - 1], oldSegments[oldSegmentCount - 1]); + Assert.Equal(resizedSegments[resizedSegmentCount - 1].Length, oldSegments[oldSegmentCount - 1].Length); + } + + [Theory] + [CombinatorialData] + public void Capacity_InOnlySingleSegment( + [CombinatorialValues(1, 2, 10, 100)] int initialCapacity, + [CombinatorialValues(1, 2, 10, 100)] int addItemCount) + { + var segmented = new SegmentedList(initialCapacity); + + var oldSegments = SegmentedCollectionsMarshal.AsSegments(segmented.GetTestAccessor().Items); + + segmented.Capacity = initialCapacity + addItemCount; + + var resizedSegments = SegmentedCollectionsMarshal.AsSegments(segmented.GetTestAccessor().Items); + + Assert.Equal(1, oldSegments.Length); + Assert.Equal(1, resizedSegments.Length); + Assert.Same(resizedSegments[0], oldSegments[0]); + Assert.Equal(segmented.Capacity, resizedSegments[0].Length); + } + + [Theory] + [InlineData(0, 1, 4)] + [InlineData(0, 10, 10)] + [InlineData(4, 6, 8)] + [InlineData(4, 10, 10)] + public void EnsureCapacity_ResizesAppropriately(int initialCapacity, int requestedCapacity, int expectedCapacity) + { + var list = new SegmentedList(initialCapacity); + + list.EnsureCapacity(requestedCapacity); + + Assert.Equal(expectedCapacity, list.Capacity); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(4)] + public void EnsureCapacity_MatchesSizeWithLargeCapacityRequest(int segmentCount) + { + var elementCount = segmentCount * SegmentedArray.TestAccessor.SegmentSize; + var list = new SegmentedList(elementCount); + + Assert.Equal(elementCount, list.Capacity); + + var requestedCapacity = 2 * elementCount + 10; + list.EnsureCapacity(requestedCapacity); + + var lastSegmentLength = requestedCapacity % SegmentedArray.TestAccessor.SegmentSize; + var expectedCapacity = (requestedCapacity - lastSegmentLength) + SegmentedArray.TestAccessor.SegmentSize; + + Assert.Equal(expectedCapacity, list.Capacity); + } + + [Fact] + public void EnsureCapacity_InitialCapacitySlightlyMoreThanHalfSegmentSizeGrowsToFullSegmentSize() + { + var elementCount = SegmentedArray.TestAccessor.SegmentSize / 2 + 1; + var list = new SegmentedList(elementCount); + + Assert.Equal(elementCount, list.Capacity); + + list.EnsureCapacity(elementCount + 1); + + Assert.Equal(SegmentedArray.TestAccessor.SegmentSize, list.Capacity); + } + } +} diff --git a/src/Compilers/Core/CodeAnalysisTest/Collections/SegmentedArrayHelperTests.cs b/src/Compilers/Core/CodeAnalysisTest/Collections/SegmentedArrayHelperTests.cs index 6580ae148a424..b9796628782d4 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Collections/SegmentedArrayHelperTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Collections/SegmentedArrayHelperTests.cs @@ -17,6 +17,9 @@ namespace Microsoft.CodeAnalysis.UnitTests.Collections { public class SegmentedArrayHelperTests { + [StructLayout(LayoutKind.Sequential, Size = 1)] + private struct Size1 { } + [StructLayout(LayoutKind.Sequential, Size = 2)] private struct Size2 { } @@ -44,10 +47,14 @@ private struct Size32 { } [StructLayout(LayoutKind.Sequential, Size = 40)] private struct Size40 { } + [StructLayout(LayoutKind.Sequential, Size = 64)] + private struct Size64 { } + public static IEnumerable ExplicitSizeTypes { get { + yield return new object[] { typeof(Size1) }; yield return new object[] { typeof(Size2) }; yield return new object[] { typeof(Size4) }; yield return new object[] { typeof(Size8) }; @@ -57,6 +64,7 @@ public static IEnumerable ExplicitSizeTypes yield return new object[] { typeof(Size28) }; yield return new object[] { typeof(Size32) }; yield return new object[] { typeof(Size40) }; + yield return new object[] { typeof(Size64) }; } } diff --git a/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets b/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets index ad407778bafb0..f11e8b1adcf90 100644 --- a/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets +++ b/src/Compilers/Core/MSBuildTask/Microsoft.CSharp.Core.targets @@ -7,31 +7,25 @@ <_MaxSupportedLangVersion Condition="('$(TargetFrameworkIdentifier)' != '.NETCoreApp' OR '$(_TargetFrameworkVersionWithoutV)' < '3.0') AND ('$(TargetFrameworkIdentifier)' != '.NETStandard' OR '$(_TargetFrameworkVersionWithoutV)' < '2.1')">7.3 - + <_MaxSupportedLangVersion Condition="(('$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(_TargetFrameworkVersionWithoutV)' < '5.0') OR ('$(TargetFrameworkIdentifier)' == '.NETStandard' AND '$(_TargetFrameworkVersionWithoutV)' == '2.1')) AND '$(_MaxSupportedLangVersion)' == ''">8.0 - - <_MaxSupportedLangVersion Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(_TargetFrameworkVersionWithoutV)' == '5.0' AND - '$(_MaxSupportedLangVersion)' == ''">9.0 - - - <_MaxSupportedLangVersion Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(_TargetFrameworkVersionWithoutV)' == '6.0' AND - '$(_MaxSupportedLangVersion)' == ''">10.0 - - - <_MaxSupportedLangVersion Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(_TargetFrameworkVersionWithoutV)' == '7.0' AND - '$(_MaxSupportedLangVersion)' == ''">11.0 - - - <_MaxSupportedLangVersion Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(_TargetFrameworkVersionWithoutV)' == '8.0' AND - '$(_MaxSupportedLangVersion)' == ''">12.0 - - - <_MaxSupportedLangVersion Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(_TargetFrameworkVersionWithoutV)' == '9.0' AND - '$(_MaxSupportedLangVersion)' == ''">13.0 + + <_MaxSupportedLangVersion Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND + '$(_MaxSupportedLangVersion)' == ''">$([MSBuild]::Add(9, $([MSBuild]::Subtract($(_TargetFrameworkVersionWithoutV), 5)))).0 + + + <_MaxAvailableLangVersion>13.0 + <_MaxSupportedLangVersion Condition="'$(_MaxSupportedLangVersion)' != '' AND + '$(_MaxSupportedLangVersion)' > '$(_MaxAvailableLangVersion)'">$(_MaxAvailableLangVersion) $(_MaxSupportedLangVersion) $(_MaxSupportedLangVersion) diff --git a/src/Compilers/Core/MSBuildTaskTests/TargetTests.cs b/src/Compilers/Core/MSBuildTaskTests/TargetTests.cs index fcc0626771d81..45c1d1a755fb3 100644 --- a/src/Compilers/Core/MSBuildTaskTests/TargetTests.cs +++ b/src/Compilers/Core/MSBuildTaskTests/TargetTests.cs @@ -403,7 +403,7 @@ public void GenerateEditorConfigCoreHandlesMalformedCompilerVisibleItemMetadata( [InlineData(".NETCoreApp", "7.0", "11.0")] [InlineData(".NETCoreApp", "8.0", "12.0")] [InlineData(".NETCoreApp", "9.0", "13.0")] - [InlineData(".NETCoreApp", "10.0", "")] + [InlineData(".NETCoreApp", "10.0", "13.0")] // update when 14.0 is released [InlineData(".NETStandard", "1.0", "7.3")] [InlineData(".NETStandard", "1.5", "7.3")] diff --git a/src/Compilers/Core/Portable/Binding/UseSiteInfo.cs b/src/Compilers/Core/Portable/Binding/UseSiteInfo.cs index 5b4d4e0667415..23ff99760ba72 100644 --- a/src/Compilers/Core/Portable/Binding/UseSiteInfo.cs +++ b/src/Compilers/Core/Portable/Binding/UseSiteInfo.cs @@ -270,14 +270,21 @@ public void AddDiagnostics(ICollection? diagnostics) foreach (var diagnosticInfo in diagnostics) { - if (_diagnostics.Add(diagnosticInfo) && diagnosticInfo?.Severity == DiagnosticSeverity.Error) - { - RecordPresenceOfAnError(); - } + AccumulateDiagnosticInfoAndRecordPresenceOfAnError(diagnosticInfo); } } } + private void AccumulateDiagnosticInfoAndRecordPresenceOfAnError(DiagnosticInfo diagnosticInfo) + { + Debug.Assert(_diagnostics is not null); + + if (_diagnostics.Add(diagnosticInfo) && diagnosticInfo?.Severity == DiagnosticSeverity.Error) + { + RecordPresenceOfAnError(); + } + } + public void AddDiagnostics(IReadOnlyCollection? diagnostics) { if (!AccumulatesDiagnostics) @@ -291,10 +298,7 @@ public void AddDiagnostics(IReadOnlyCollection? diagnostics) foreach (var diagnosticInfo in diagnostics) { - if (_diagnostics.Add(diagnosticInfo) && diagnosticInfo?.Severity == DiagnosticSeverity.Error) - { - RecordPresenceOfAnError(); - } + AccumulateDiagnosticInfoAndRecordPresenceOfAnError(diagnosticInfo); } } } @@ -312,14 +316,23 @@ public void AddDiagnostics(ImmutableArray diagnostics) foreach (var diagnosticInfo in diagnostics) { - if (_diagnostics.Add(diagnosticInfo) && diagnosticInfo?.Severity == DiagnosticSeverity.Error) - { - RecordPresenceOfAnError(); - } + AccumulateDiagnosticInfoAndRecordPresenceOfAnError(diagnosticInfo); } } } + public void AddDiagnosticInfo(DiagnosticInfo diagnosticInfo) + { + if (!AccumulatesDiagnostics) + { + return; + } + + _diagnostics ??= new HashSet(); + + AccumulateDiagnosticInfoAndRecordPresenceOfAnError(diagnosticInfo); + } + public void AddDependencies(UseSiteInfo info) { if (!_hasErrors && AccumulatesDependencies) diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.resx b/src/Compilers/Core/Portable/CodeAnalysisResources.resx index c013f246a5a59..6dd5fc6a12d31 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.resx +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.resx @@ -794,4 +794,7 @@ '{0}' type does not have the expected constructor + + {0}; file may be locked by {1} + \ No newline at end of file diff --git a/src/Compilers/Core/Portable/CodeGen/CompilationTestData.cs b/src/Compilers/Core/Portable/CodeGen/CompilationTestData.cs index b57b9d31da861..3797649644974 100644 --- a/src/Compilers/Core/Portable/CodeGen/CompilationTestData.cs +++ b/src/Compilers/Core/Portable/CodeGen/CompilationTestData.cs @@ -86,7 +86,7 @@ public ImmutableDictionary GetMethodsByName() private static readonly SymbolDisplayFormat _testDataKeyFormat = new SymbolDisplayFormat( compilerInternalOptions: - SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames | + SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames | SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes, globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining, typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, diff --git a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.CompilerEmitStreamProvider.cs b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.CompilerEmitStreamProvider.cs index f0cc3f26907d5..882906e7540c9 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.CompilerEmitStreamProvider.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.CompilerEmitStreamProvider.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Linq; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -114,7 +115,18 @@ private Stream OpenFileStream() private void ReportOpenFileDiagnostic(DiagnosticBag diagnostics, Exception e) { var messageProvider = _compiler.MessageProvider; - diagnostics.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_CantOpenFileWrite, Location.None, _filePath, e.Message)); + var lockedBy = FileLockCheck.TryGetLockingProcessInfos(_filePath); + + var message = lockedBy.IsEmpty + ? (object)e.Message + : new LocalizableResourceString( + nameof(CodeAnalysisResources.ExceptionMessage_FileMayBeLockedBy), + CodeAnalysisResources.ResourceManager, + typeof(CodeAnalysisResources), + e.Message, + string.Join(", ", lockedBy.Select(info => $"'{info.applicationName}' ({info.processId})"))); + + diagnostics.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_CantOpenFileWrite, Location.None, _filePath, message)); } } } diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index b6a82988ee0e5..b7210ad2b93fa 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -379,6 +379,14 @@ public INamespaceSymbol CreateErrorNamespaceSymbol(INamespaceSymbol container, s protected abstract INamespaceSymbol CommonCreateErrorNamespaceSymbol(INamespaceSymbol container, string name); + /// + /// Returns a new IPreprocessingSymbol representing a preprocessing symbol with the given name. + /// + public IPreprocessingSymbol CreatePreprocessingSymbol(string name) + => CommonCreatePreprocessingSymbol(name ?? throw new ArgumentNullException(nameof(name))); + + protected abstract IPreprocessingSymbol CommonCreatePreprocessingSymbol(string name); + #region Name internal const string UnspecifiedModuleAssemblyName = "?"; diff --git a/src/Compilers/Core/Portable/InternalUtilities/FileLockCheck.cs b/src/Compilers/Core/Portable/InternalUtilities/FileLockCheck.cs new file mode 100644 index 0000000000000..3238c827b0694 --- /dev/null +++ b/src/Compilers/Core/Portable/InternalUtilities/FileLockCheck.cs @@ -0,0 +1,227 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// Based on https://github.com/dotnet/msbuild/blob/6cd445d84e59a36c7fbb6f50b7a5a62767a6da51/src/Utilities/LockCheck.cs + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Roslyn.Utilities; + +/// +/// This class implements checking what processes are locking a file on Windows. +/// It uses the Restart Manager API to do this. Other platforms are skipped. +/// +internal static class FileLockCheck +{ + [StructLayout(LayoutKind.Sequential)] + private struct FILETIME + { + public uint dwLowDateTime; + public uint dwHighDateTime; + } + + [StructLayout(LayoutKind.Sequential)] + private struct RM_UNIQUE_PROCESS + { + public uint dwProcessId; + public FILETIME ProcessStartTime; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + private struct RM_PROCESS_INFO + { + private const int CCH_RM_MAX_APP_NAME = 255; + private const int CCH_RM_MAX_SVC_NAME = 63; + + internal RM_UNIQUE_PROCESS Process; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)] + public string strAppName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)] + public string strServiceShortName; + internal int ApplicationType; + public uint AppStatus; + public uint TSSessionId; + [MarshalAs(UnmanagedType.Bool)] + public bool bRestartable; + } + + private const string RestartManagerDll = "rstrtmgr.dll"; + + [DllImport(RestartManagerDll, CharSet = CharSet.Unicode)] + private static extern int RmRegisterResources(uint pSessionHandle, + uint nFiles, + string[] rgsFilenames, + uint nApplications, + [In] RM_UNIQUE_PROCESS[]? rgApplications, + uint nServices, + string[]? rgsServiceNames); + + /// + /// Starts a new Restart Manager session. + /// A maximum of 64 Restart Manager sessions per user session + /// can be open on the system at the same time. When this + /// function starts a session, it returns a session handle + /// and session key that can be used in subsequent calls to + /// the Restart Manager API. + /// + /// + /// A pointer to the handle of a Restart Manager session. + /// The session handle can be passed in subsequent calls + /// to the Restart Manager API. + /// + /// + /// Reserved. This parameter should be 0. + /// + /// + /// A null-terminated string that contains the session key + /// to the new session. The string must be allocated before + /// calling the RmStartSession function. + /// + /// System error codes that are defined in Winerror.h. + /// + /// The Rm­­StartSession function doesn’t properly null-terminate + /// the session key, even though the function is documented as + /// returning a null-terminated string. To work around this bug, + /// we pre-fill the buffer with null characters so that whatever + /// ends gets written will have a null terminator (namely, one of + /// the null characters we placed ahead of time). + /// + /// see . + /// + /// + [DllImport(RestartManagerDll, CharSet = CharSet.Unicode)] + private static extern unsafe int RmStartSession( + out uint pSessionHandle, + int dwSessionFlags, + char* strSessionKey); + + /// + /// Ends the Restart Manager session. + /// This function should be called by the primary installer that + /// has previously started the session by calling the + /// function. The RmEndSession function can be called by a secondary installer + /// that is joined to the session once no more resources need to be registered + /// by the secondary installer. + /// + /// A handle to an existing Restart Manager session. + /// + /// The function can return one of the system error codes that are defined in Winerror.h. + /// + [DllImport(RestartManagerDll)] + private static extern int RmEndSession(uint pSessionHandle); + + [DllImport(RestartManagerDll, CharSet = CharSet.Unicode)] + private static extern int RmGetList(uint dwSessionHandle, + out uint pnProcInfoNeeded, + ref uint pnProcInfo, + [In, Out] RM_PROCESS_INFO[]? rgAffectedApps, + ref uint lpdwRebootReasons); + + public static ImmutableArray<(int processId, string applicationName)> TryGetLockingProcessInfos(string path) + { + if (!PlatformInformation.IsWindows) + { + return []; + } + + try + { + return GetLockingProcessInfosImpl([path]); + } + catch + { + return []; + } + } + + private static ImmutableArray<(int processId, string applicationName)> GetLockingProcessInfosImpl(string[] paths) + { + const int MaxRetries = 6; + const int ERROR_MORE_DATA = 234; + const uint RM_REBOOT_REASON_NONE = 0; + + uint handle; + int res; + + unsafe + { + char* key = stackalloc char[sizeof(Guid) * 2 + 1]; + res = RmStartSession(out handle, 0, key); + } + + if (res != 0) + { + return []; + } + + try + { + res = RmRegisterResources(handle, (uint)paths.Length, paths, 0, null, 0, null); + if (res != 0) + { + return []; + } + + // + // Obtain the list of affected applications/services. + // + // NOTE: Restart Manager returns the results into the buffer allocated by the caller. The first call to + // RmGetList() will return the size of the buffer (i.e. nProcInfoNeeded) the caller needs to allocate. + // The caller then needs to allocate the buffer (i.e. rgAffectedApps) and make another RmGetList() + // call to ask Restart Manager to write the results into the buffer. However, since Restart Manager + // refreshes the list every time RmGetList()is called, it is possible that the size returned by the first + // RmGetList()call is not sufficient to hold the results discovered by the second RmGetList() call. Therefore, + // it is recommended that the caller follows the following practice to handle this race condition: + // + // Use a loop to call RmGetList() in case the buffer allocated according to the size returned in previous + // call is not enough. + // + uint pnProcInfo = 0; + RM_PROCESS_INFO[]? rgAffectedApps = null; + int retry = 0; + do + { + uint lpdwRebootReasons = RM_REBOOT_REASON_NONE; + res = RmGetList(handle, out uint pnProcInfoNeeded, ref pnProcInfo, rgAffectedApps, ref lpdwRebootReasons); + if (res == 0) + { + // If pnProcInfo == 0, then there is simply no locking process (found), in this case rgAffectedApps is "null". + if (pnProcInfo == 0) + { + return []; + } + + Debug.Assert(rgAffectedApps != null); + + var lockInfos = ArrayBuilder<(int, string)>.GetInstance((int)pnProcInfo); + for (int i = 0; i < pnProcInfo; i++) + { + lockInfos.Add(((int)rgAffectedApps[i].Process.dwProcessId, rgAffectedApps[i].strAppName)); + } + + return lockInfos.ToImmutableAndFree(); + } + + if (res != ERROR_MORE_DATA) + { + return []; + } + + pnProcInfo = pnProcInfoNeeded; + rgAffectedApps = new RM_PROCESS_INFO[pnProcInfo]; + } + while (res == ERROR_MORE_DATA && retry++ < MaxRetries); + } + finally + { + _ = RmEndSession(handle); + } + + return []; + } +} diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 4a90d1416e3c9..e507230349b2a 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -19,6 +19,7 @@ Microsoft.CodeAnalysis.ITypeParameterSymbol.AllowsRefLikeType.get -> bool Microsoft.CodeAnalysis.RuntimeCapability.ByRefLikeGenerics = 8 -> Microsoft.CodeAnalysis.RuntimeCapability static Microsoft.CodeAnalysis.GeneratorExtensions.AsIncrementalGenerator(this Microsoft.CodeAnalysis.ISourceGenerator! sourceGenerator) -> Microsoft.CodeAnalysis.IIncrementalGenerator! static Microsoft.CodeAnalysis.GeneratorExtensions.GetGeneratorType(this Microsoft.CodeAnalysis.IIncrementalGenerator! generator) -> System.Type! +Microsoft.CodeAnalysis.Compilation.CreatePreprocessingSymbol(string! name) -> Microsoft.CodeAnalysis.IPreprocessingSymbol! [RSEXPERIMENTAL004]Microsoft.CodeAnalysis.HostOutputProductionContext [RSEXPERIMENTAL004]Microsoft.CodeAnalysis.HostOutputProductionContext.AddOutput(string! name, object! value) -> void [RSEXPERIMENTAL004]Microsoft.CodeAnalysis.HostOutputProductionContext.CancellationToken.get -> System.Threading.CancellationToken diff --git a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs index b7ecd7c4f9f6b..0acb9aa43f5fb 100644 --- a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs +++ b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayCompilerInternalOptions.cs @@ -17,9 +17,10 @@ internal enum SymbolDisplayCompilerInternalOptions None = 0, /// - /// ".ctor" instead of "Goo" + /// - ".ctor" instead of "Goo" + /// - "<Prop>k__backingField" instead of "Prop.field" (for C# backing fields) /// - UseMetadataMethodNames = 1 << 0, + UseMetadataMemberNames = 1 << 0, /// /// "List`1" instead of "List<T>" ("List(of T)" in VB). Overrides GenericsOptions on diff --git a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayFormat.cs b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayFormat.cs index 5e0ba2c34e6ca..20f1a5586c8b8 100644 --- a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayFormat.cs +++ b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayFormat.cs @@ -199,7 +199,7 @@ public class SymbolDisplayFormat SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier, compilerInternalOptions: SymbolDisplayCompilerInternalOptions.IncludeScriptType | - SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames | + SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames | SymbolDisplayCompilerInternalOptions.FlagMissingMetadataTypes | SymbolDisplayCompilerInternalOptions.IncludeCustomModifiers | SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes); @@ -257,7 +257,7 @@ public class SymbolDisplayFormat miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes | SymbolDisplayMiscellaneousOptions.ExpandValueTuple, - compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames); + compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames); /// /// Used to normalize explicit interface implementation member names. diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/CommonMethodEarlyWellKnownAttributeData.cs b/src/Compilers/Core/Portable/Symbols/Attributes/CommonMethodEarlyWellKnownAttributeData.cs index eaf500643a99b..2cf7763f5dccd 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/CommonMethodEarlyWellKnownAttributeData.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/CommonMethodEarlyWellKnownAttributeData.cs @@ -76,9 +76,9 @@ public bool HasSetsRequiredMembersAttribute #endregion #region OverloadResolutionPriorityAttribute - private int? _overloadResolutionPriority = null; + private int _overloadResolutionPriority = 0; [DisallowNull] - public int? OverloadResolutionPriority + public int OverloadResolutionPriority { get { @@ -88,7 +88,6 @@ public int? OverloadResolutionPriority set { VerifySealed(expected: false); - Debug.Assert(value != null); _overloadResolutionPriority = value; SetDataStored(); } diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/CommonPropertyEarlyWellKnownAttributeData.cs b/src/Compilers/Core/Portable/Symbols/Attributes/CommonPropertyEarlyWellKnownAttributeData.cs index 5582c14667ef9..f4f2036cf4a75 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/CommonPropertyEarlyWellKnownAttributeData.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/CommonPropertyEarlyWellKnownAttributeData.cs @@ -38,9 +38,9 @@ public ObsoleteAttributeData? ObsoleteAttributeData #endregion #region OverloadResolutionPriorityAttribute - private int? _overloadResolutionPriority = null; + private int _overloadResolutionPriority = 0; [DisallowNull] - public int? OverloadResolutionPriority + public int OverloadResolutionPriority { get { @@ -50,7 +50,6 @@ public int? OverloadResolutionPriority set { VerifySealed(expected: false); - Debug.Assert(value != null); _overloadResolutionPriority = value; SetDataStored(); } diff --git a/src/Compilers/Core/Portable/Symbols/INamedTypeSymbolInternal.cs b/src/Compilers/Core/Portable/Symbols/INamedTypeSymbolInternal.cs index 9bbab4d281ebf..31e9f10ee96d6 100644 --- a/src/Compilers/Core/Portable/Symbols/INamedTypeSymbolInternal.cs +++ b/src/Compilers/Core/Portable/Symbols/INamedTypeSymbolInternal.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Diagnostics; namespace Microsoft.CodeAnalysis.Symbols { @@ -16,5 +17,69 @@ internal interface INamedTypeSymbolInternal : ITypeSymbolInternal ImmutableArray GetMembers(); ImmutableArray GetMembers(string name); + + /// + /// True if this type or some containing type has type parameters. + /// + bool IsGenericType { get; } + + internal static class Helpers + { + /// + /// Returns True or False if we can determine whether the type is managed + /// without looking at its fields and Unknown otherwise. + /// Also returns whether or not the given type is generic. + /// + public static (ThreeState isManaged, bool hasGenerics) IsManagedTypeHelper(INamedTypeSymbolInternal type) + { + // To match dev10, we treat enums as their underlying types. + if (type.TypeKind == TypeKind.Enum) + { + Debug.Assert(type.EnumUnderlyingType is not null); + type = type.EnumUnderlyingType; + } + + // Short-circuit common cases. + switch (type.SpecialType) + { + case SpecialType.System_Void: + case SpecialType.System_Boolean: + case SpecialType.System_Char: + case SpecialType.System_SByte: + case SpecialType.System_Byte: + case SpecialType.System_Int16: + case SpecialType.System_UInt16: + case SpecialType.System_Int32: + case SpecialType.System_UInt32: + case SpecialType.System_Int64: + case SpecialType.System_UInt64: + case SpecialType.System_Decimal: + case SpecialType.System_Single: + case SpecialType.System_Double: + case SpecialType.System_IntPtr: + case SpecialType.System_UIntPtr: + case SpecialType.System_ArgIterator: + case SpecialType.System_RuntimeArgumentHandle: + return (ThreeState.False, false); + case SpecialType.System_TypedReference: + return (ThreeState.True, false); + case SpecialType.None: + default: + // CONSIDER: could provide cases for other common special types. + break; // Proceed with additional checks. + } + + bool hasGenerics = type.IsGenericType; + switch (type.TypeKind) + { + case TypeKind.Enum: + return (ThreeState.False, hasGenerics); + case TypeKind.Struct: + return (ThreeState.Unknown, hasGenerics); + default: + return (ThreeState.True, hasGenerics); + } + } + } } } diff --git a/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs b/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs index 0516496a3a2f0..d32a133d8c310 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs @@ -109,5 +109,10 @@ public virtual void VisitTypeParameter(ITypeParameterSymbol symbol) { DefaultVisit(symbol); } + + internal virtual void VisitPreprocessing(IPreprocessingSymbol symbol) + { + DefaultVisit(symbol); + } } } diff --git a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs index 119e8be223cdf..212da5801cfe0 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`1.cs @@ -112,5 +112,10 @@ public abstract class SymbolVisitor { return DefaultVisit(symbol); } + + internal virtual TResult? VisitPreprocessing(IPreprocessingSymbol symbol) + { + return DefaultVisit(symbol); + } } } diff --git a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs index ea9d2eba5e8fd..46681a1e8ca3c 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolVisitor`2.cs @@ -117,5 +117,10 @@ public virtual TResult VisitTypeParameter(ITypeParameterSymbol symbol, TArgument { return DefaultVisit(symbol, argument); } + + internal virtual TResult VisitPreprocessing(IPreprocessingSymbol symbol, TArgument argument) + { + return DefaultVisit(symbol, argument); + } } } diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs index 8970c644528ed..33ece21e2439e 100644 --- a/src/Compilers/Core/Portable/WellKnownMember.cs +++ b/src/Compilers/Core/Portable/WellKnownMember.cs @@ -625,6 +625,7 @@ internal enum WellKnownMember System_Runtime_CompilerServices_ParamCollectionAttribute__ctor, System_Linq_Enumerable__ToList, + System_Linq_Enumerable__ToArray, System_Linq_Expressions_Expression__ArrayIndex_Expression_Expression, System_Linq_Expressions_Expression__ArrayIndex_Expression_Expressions, diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs index 4bf9c0c9905f6..37cf53ebccdb4 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -4352,6 +4352,17 @@ static WellKnownMembers() 1, (byte)SignatureTypeCode.GenericMethodParameter, 0, + // System_Linq_Enumerable__ToArray + (byte)(MemberFlags.Method | MemberFlags.Static), // Flags + (byte)WellKnownType.System_Linq_Enumerable, // DeclaringTypeId + 1, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.GenericMethodParameter, 0, // Return Type + (byte)SignatureTypeCode.GenericTypeInstance, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Collections_Generic_IEnumerable_T, + 1, + (byte)SignatureTypeCode.GenericMethodParameter, 0, + // System_Linq_Expressions_Expression__ArrayIndex_Expression_Expression, (byte)(MemberFlags.Method | MemberFlags.Static), // Flags (byte)WellKnownType.System_Linq_Expressions_Expression, // DeclaringTypeId @@ -5706,6 +5717,7 @@ static WellKnownMembers() "AddRange", // System_Collections_Generic_List_T__AddRange ".ctor", // System_Runtime_CompilerServices_ParamCollectionAttribute__ctor "ToList", // System_Linq_Enumerable__ToList + "ToArray", // System_Linq_Enumerable__ToArray "ArrayIndex", // System_Linq_Expressions_Expression__ArrayIndex_Expression_Expression "ArrayIndex", // System_Linq_Expressions_Expression__ArrayIndex_Expression_Expressions "Constant", // System_Linq_Expressions_Expression__Constant diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf index 2df3ee048a081..8c78a670fca3d 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.cs.xlf @@ -87,6 +87,11 @@ Hodnota end nesmí být menší než hodnota start. start={0} end={1}. + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Generátor diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.de.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.de.xlf index 94bb96efb0917..09105c3c8acdc 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.de.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.de.xlf @@ -87,6 +87,11 @@ "Ende" darf nicht kleiner sein als "Start". start='{0}' end='{1}'. + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Generator diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.es.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.es.xlf index 3c49dabe1c642..464d9582d2581 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.es.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.es.xlf @@ -87,6 +87,11 @@ 'fin' no debe ser menor que 'inicio'. inicio='{0}' fin='{1}'. + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Generador diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.fr.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.fr.xlf index 8364a6e0d57d7..cd0e0e2f40022 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.fr.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.fr.xlf @@ -87,6 +87,11 @@ 'end' ne doit pas être inférieur à 'start'. start='{0}'end='{1}'. + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Générateur diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.it.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.it.xlf index 4e1ea7458a093..9bff0aac1c488 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.it.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.it.xlf @@ -87,6 +87,11 @@ il valore di 'end' non deve essere minore di quello di 'start'. start='{0}' end='{1}'. + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Generatore diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ja.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ja.xlf index bbe59f873bdcb..7e6eaf727cd1d 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ja.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ja.xlf @@ -87,6 +87,11 @@ 'end' は 'start' より小さくすることはできません。start='{0}' end='{1}'。 + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator ジェネレーター diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ko.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ko.xlf index db8f19aed58d0..b974f0d815418 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ko.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ko.xlf @@ -87,6 +87,11 @@ 'end'는 'start'보다 작을 수 없습니다. start='{0}' end='{1}' + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator 생성기 diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pl.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pl.xlf index cc98075bccb34..8afb64f762d90 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pl.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pl.xlf @@ -87,6 +87,11 @@ Wartość „end” nie może być mniejsza niż wartość „start”. start=„{0}” end=„{1}”. + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Generator diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pt-BR.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pt-BR.xlf index 52a2d40046885..667da8fa8a4ff 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pt-BR.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.pt-BR.xlf @@ -87,6 +87,11 @@ 'end' não deve ser menor que 'start'. start='{0}' end='{1}'. + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Gerador diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ru.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ru.xlf index 7137861c3cee9..d73c99504d656 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ru.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.ru.xlf @@ -87,6 +87,11 @@ Значение "end" не должно быть меньше, чем "start". start="{0}", end="{1}". + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Генератор diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.tr.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.tr.xlf index c18509b928aee..7b5f10d12a00d 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.tr.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.tr.xlf @@ -87,6 +87,11 @@ 'bitiş', 'başlangıç' değerinden küçük olmamalıdır. başla='{0}' bitiş='{1}'. + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator Oluşturucu diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hans.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hans.xlf index 195a98abb1b34..db00dcb951da4 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hans.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hans.xlf @@ -87,6 +87,11 @@ "end" 不得小于 "start"。start="{0}" end="{1}"。 + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator 生成器 diff --git a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hant.xlf b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hant.xlf index d0daf896b2cdd..15e41de1aac64 100644 --- a/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hant.xlf +++ b/src/Compilers/Core/Portable/xlf/CodeAnalysisResources.zh-Hant.xlf @@ -87,6 +87,11 @@ 'end' 不可小於 'start'。start='{0}' end='{1}'。 + + {0}; file may be locked by {1} + {0}; file may be locked by {1} + + Generator 產生器 diff --git a/src/Compilers/Core/RebuildTest/BasicDeterministicKeyBuilderTests.cs b/src/Compilers/Core/RebuildTest/BasicDeterministicKeyBuilderTests.cs index 24e0c4f6122c4..f6977a5e218fd 100644 --- a/src/Compilers/Core/RebuildTest/BasicDeterministicKeyBuilderTests.cs +++ b/src/Compilers/Core/RebuildTest/BasicDeterministicKeyBuilderTests.cs @@ -220,7 +220,7 @@ public void BasicPathMapWindows(string filePath, string workingDirectory, string ""specifiedLanguageVersion"": ""VisualBasic15"", ""preprocessorSymbols"": { ""TARGET"": ""exe"", - ""VBC_VER"": ""16.9"" + ""VBC_VER"": ""17.13"" } } } @@ -360,7 +360,7 @@ public void FeatureFlag() ""specifiedLanguageVersion"": ""Default"", ""preprocessorSymbols"": {{ ""TARGET"": ""library"", - ""VBC_VER"": ""16.9"", + ""VBC_VER"": ""17.13"", ""_MYTYPE"": ""Empty"" }} }} @@ -385,7 +385,7 @@ public void FeatureFlag() ""specifiedLanguageVersion"": ""Default"", ""preprocessorSymbols"": {{ ""TARGET"": ""library"", - ""VBC_VER"": ""16.9"", + ""VBC_VER"": ""17.13"", ""_MYTYPE"": ""Empty"" }} }} diff --git a/src/Compilers/Server/VBCSCompilerTests/BuildClientTests.cs b/src/Compilers/Server/VBCSCompilerTests/BuildClientTests.cs index bca3ced3dcea0..53c327be7866a 100644 --- a/src/Compilers/Server/VBCSCompilerTests/BuildClientTests.cs +++ b/src/Compilers/Server/VBCSCompilerTests/BuildClientTests.cs @@ -344,6 +344,14 @@ public void GetPipeNameForPathOptLength() // We only have ~50 total bytes to work with on mac, so the base path must be small Assert.Equal(43, name.Length); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75714")] + public void GetPipeNameForPath_Casing() + { + var path1 = string.Format(@"q:{0}the{0}path", Path.DirectorySeparatorChar); + var path2 = string.Format(@"Q:{0}The{0}Path", Path.DirectorySeparatorChar); + Assert.Equal(BuildServerConnection.GetPipeName(path1), BuildServerConnection.GetPipeName(path2)); + } } } } diff --git a/src/Compilers/Shared/BuildServerConnection.cs b/src/Compilers/Shared/BuildServerConnection.cs index 2803cd46929ce..c8596ba8b7dc1 100644 --- a/src/Compilers/Shared/BuildServerConnection.cs +++ b/src/Compilers/Shared/BuildServerConnection.cs @@ -564,6 +564,9 @@ internal static string GetPipeName( // of this method. clientDirectory = clientDirectory.TrimEnd(Path.DirectorySeparatorChar); + // Similarly, we don't want multiple servers if the provided launch path differs in casing. + clientDirectory = clientDirectory.ToLowerInvariant(); + var pipeNameInput = $"{userName}.{isAdmin}.{clientDirectory}"; using (var sha = SHA256.Create()) { diff --git a/src/Compilers/Test/Core/Assert/AssertEx.cs b/src/Compilers/Test/Core/Assert/AssertEx.cs index 49a41a88e565e..cbe7bc403bd09 100644 --- a/src/Compilers/Test/Core/Assert/AssertEx.cs +++ b/src/Compilers/Test/Core/Assert/AssertEx.cs @@ -182,15 +182,15 @@ public static void Equal(ImmutableArray expected, IEnumerable actual, I public static void Equal(IEnumerable expected, ImmutableArray actual) => Equal(expected, actual, comparer: null, message: null, itemInspector: null); - public static void Equal(IEnumerable expected, ImmutableArray actual, IEqualityComparer comparer = null, string message = null, string itemSeparator = null) + public static void SequenceEqual(IEnumerable expected, IEnumerable actual, IEqualityComparer comparer = null, string message = null, string itemSeparator = null) { - if (expected == null || actual.IsDefault) + if (expected == null || actual == null) { - Assert.True((expected == null) == actual.IsDefault, message); + Assert.True(expected is null == actual is null, message); } else { - Equal(expected, (IEnumerable)actual, comparer, message, itemSeparator); + Equal(expected, actual, comparer, message, itemSeparator); } } diff --git a/src/Compilers/Test/Core/Assert/ConditionalFactAttribute.cs b/src/Compilers/Test/Core/Assert/ConditionalFactAttribute.cs index 793ad240bc774..34c52322212ed 100644 --- a/src/Compilers/Test/Core/Assert/ConditionalFactAttribute.cs +++ b/src/Compilers/Test/Core/Assert/ConditionalFactAttribute.cs @@ -159,13 +159,12 @@ public static class ExecutionConditionUtil public static bool IsWindowsDesktop => IsWindows && IsDesktop; // IsMonoDesktop means https://github.com/mono/mono public static bool IsMonoDesktop => Type.GetType("Mono.Runtime") != null; - public static bool IsMono => MonoHelpers.IsRunningOnMono(); // IsMonoCore means https://github.com/dotnet/runtime/tree/main/src/mono public static bool IsMonoCore => Type.GetType("Mono.RuntimeStructs") != null; public static bool IsAnyMono => IsMonoCore || IsMonoDesktop; public static bool IsCoreClr => !IsDesktop; public static bool IsCoreClrUnix => IsCoreClr && IsUnix; - public static bool IsMonoOrCoreClr => IsMono || IsCoreClr; + public static bool IsMonoOrCoreClr => IsMonoDesktop || IsCoreClr; public static bool RuntimeSupportsCovariantReturnsOfClasses => Type.GetType("System.Runtime.CompilerServices.RuntimeFeature")?.GetField("CovariantReturnsOfClasses") != null; private static readonly Lazy s_operatingSystemRestrictsFileNames = new Lazy(() => diff --git a/src/Compilers/Test/Core/InstrumentationChecker.cs b/src/Compilers/Test/Core/InstrumentationChecker.cs index 8691cf0134f16..6427ec517890e 100644 --- a/src/Compilers/Test/Core/InstrumentationChecker.cs +++ b/src/Compilers/Test/Core/InstrumentationChecker.cs @@ -335,7 +335,7 @@ public void CompleteCheck(Compilation compilation, string source) var actualSnippets = GetActualSnippets(method, reader, sourceLines); var expectedSnippets = _spanExpectations[method].SnippetExpectations; - AssertEx.Equal(expectedSnippets, actualSnippets, new SnippetComparer(), $"Validation of method {method} failed."); + AssertEx.SequenceEqual(expectedSnippets, actualSnippets, new SnippetComparer(), $"Validation of method {method} failed."); } } diff --git a/src/Compilers/Test/Core/Traits/Traits.cs b/src/Compilers/Test/Core/Traits/Traits.cs index 0aeb366503abe..cced8ae0268cd 100644 --- a/src/Compilers/Test/Core/Traits/Traits.cs +++ b/src/Compilers/Test/Core/Traits/Traits.cs @@ -218,6 +218,8 @@ public static class Features public const string CodeActionsUseSystemHashCode = "CodeActions.UseSystemHashCode"; public const string CodeActionsUseSystemThreadingLock = "CodeActions.UseSystemThreadingLock"; public const string CodeActionsUseThrowExpression = "CodeActions.UseThrowExpression"; + public const string CodeActionsUseTupleSwap = "CodeActions.UseTupleSwap"; + public const string CodeActionsUseUnboundGenericTypeInNameOf = "CodeActions.UseUnboundGenericTypeInNameOf"; public const string CodeActionsUseUtf8StringLiteral = "CodeActions.CodeActionsUseUtf8StringLiteral"; public const string CodeActionsWrapping = "CodeActions.Wrapping"; public const string CodeCleanup = nameof(CodeCleanup); diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index e5bc18a51842e..a2d5397815847 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -587,7 +587,7 @@ public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter } "; - protected static readonly string AsyncStreamsTypes = DisposableAsyncEnumeratorDefinition + CommonAsyncStreamsTypes; + public static readonly string AsyncStreamsTypes = DisposableAsyncEnumeratorDefinition + CommonAsyncStreamsTypes; protected static readonly string EnumeratorCancellationAttributeType = @" namespace System.Runtime.CompilerServices diff --git a/src/Compilers/Test/Utilities/VisualBasic/BasicTestSource.vb b/src/Compilers/Test/Utilities/VisualBasic/BasicTestSource.vb index dd88a392d4954..6fd897b5505f2 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/BasicTestSource.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/BasicTestSource.vb @@ -43,12 +43,17 @@ Public Structure BasicTestSource Dim source = TryCast(Value, String) If source IsNot Nothing Then - Return New SyntaxTree() {VisualBasicSyntaxTree.ParseText(SourceText.From(source, encoding:=Nothing, SourceHashAlgorithms.Default), parseOptions)} + Return New SyntaxTree() _ + { + VisualBasicSyntaxTree.ParseText( + SourceText.From(source, encoding:=Nothing, SourceHashAlgorithms.Default), + If(parseOptions, TestOptions.RegularLatest)) + } End If Dim sources = TryCast(Value, String()) If sources IsNot Nothing Then - Return sources.Select(Function(s) VisualBasicSyntaxTree.ParseText(SourceText.From(s, encoding:=Nothing, SourceHashAlgorithms.Default), parseOptions)).ToArray() + Return sources.Select(Function(s) VisualBasicSyntaxTree.ParseText(SourceText.From(s, encoding:=Nothing, SourceHashAlgorithms.Default), If(parseOptions, TestOptions.RegularLatest))).ToArray() End If Dim tree = TryCast(Value, SyntaxTree) diff --git a/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb b/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb index 92534ab7a301f..93f4e83be9b84 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/CompilationTestUtils.vb @@ -651,7 +651,7 @@ Friend Module CompilationUtils MarkupTestFile.GetSpans(codeWithMarker, codeWithoutMarker, spans) Dim text = SourceText.From(codeWithoutMarker, Encoding.UTF8) - Return (VisualBasicSyntaxTree.ParseText(text, parseOptions, If(programElement.@name, "")), spans) + Return (VisualBasicSyntaxTree.ParseText(text, If(parseOptions, TestOptions.RegularLatest), If(programElement.@name, "")), spans) End Function ' Find a node inside a tree. diff --git a/src/Compilers/Test/Utilities/VisualBasic/Extensions.vb b/src/Compilers/Test/Utilities/VisualBasic/Extensions.vb index 74017f93d7cc8..f8fb2e210426a 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/Extensions.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/Extensions.vb @@ -344,12 +344,12 @@ Friend Module Extensions Friend Function ReduceExtensionMethod(this As MethodSymbol, instanceType As TypeSymbol) As MethodSymbol - Return this.ReduceExtensionMethod(instanceType, CompoundUseSiteInfo(Of AssemblySymbol).Discarded) + Return this.ReduceExtensionMethod(instanceType, CompoundUseSiteInfo(Of AssemblySymbol).Discarded, LanguageVersion.Latest) End Function Friend Function ReduceExtensionMethod(this As MethodSymbol, instanceType As TypeSymbol, proximity As Integer) As MethodSymbol - Return this.ReduceExtensionMethod(instanceType, proximity, CompoundUseSiteInfo(Of AssemblySymbol).Discarded) + Return this.ReduceExtensionMethod(instanceType, proximity, CompoundUseSiteInfo(Of AssemblySymbol).Discarded, LanguageVersion.Latest) End Function diff --git a/src/Compilers/Test/Utilities/VisualBasic/ParserTestUtilities.vb b/src/Compilers/Test/Utilities/VisualBasic/ParserTestUtilities.vb index 005330a70f15c..1858d6987c0d7 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/ParserTestUtilities.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/ParserTestUtilities.vb @@ -110,7 +110,7 @@ Friend Module ParserTestUtilities encoding = Encoding.UTF8 End If - Dim tree = VisualBasicSyntaxTree.ParseText(SourceText.From(source, encoding), options:=If(options, VisualBasicParseOptions.Default), path:=fileName) + Dim tree = VisualBasicSyntaxTree.ParseText(SourceText.From(source, encoding), options:=If(options, TestOptions.RegularLatest), path:=fileName) Dim root = tree.GetRoot() ' Verify FullText Assert.Equal(source, root.ToFullString) diff --git a/src/Compilers/Test/Utilities/VisualBasic/TestOptions.vb b/src/Compilers/Test/Utilities/VisualBasic/TestOptions.vb index 84c370aa213f7..875f9fd5029a2 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/TestOptions.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/TestOptions.vb @@ -14,6 +14,7 @@ Public Class TestOptions Public Shared ReadOnly Regular15_5 As VisualBasicParseOptions = Regular.WithLanguageVersion(LanguageVersion.VisualBasic15_5) Public Shared ReadOnly Regular16 As VisualBasicParseOptions = Regular.WithLanguageVersion(LanguageVersion.VisualBasic16) Public Shared ReadOnly Regular16_9 As VisualBasicParseOptions = Regular.WithLanguageVersion(LanguageVersion.VisualBasic16_9) + Public Shared ReadOnly Regular17_13 As VisualBasicParseOptions = Regular.WithLanguageVersion(LanguageVersion.VisualBasic17_13) Public Shared ReadOnly RegularLatest As VisualBasicParseOptions = Regular.WithLanguageVersion(LanguageVersion.Latest) Public Shared ReadOnly RegularWithLegacyStrongName As VisualBasicParseOptions = Regular.WithFeature("UseLegacyStrongNameProvider") diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder.vb index f768e7f1ee1b8..a5d5bc1becfb0 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder.vb @@ -1029,6 +1029,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Select Case Me.BindingLocation Case BindingLocation.BaseTypes, BindingLocation.MethodSignature, + BindingLocation.FieldType, + BindingLocation.PropertyType, + BindingLocation.EventType, BindingLocation.GenericConstraintsClause, BindingLocation.ProjectImportsDeclaration, BindingLocation.SourceFileImportsDeclaration diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Invocation.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Invocation.vb index 4b6fc722871a4..a1aa76da2ac0e 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Invocation.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Invocation.vb @@ -2274,7 +2274,7 @@ ProduceBoundNode: Dim method = DirectCast(candidate.UnderlyingSymbol, MethodSymbol) ' TODO: Dev10 uses the location of the type parameter or argument that ' violated the constraint, rather than the entire invocation expression. - Dim succeeded = method.CheckConstraints(diagnosticLocation, diagnostics, template:=GetNewCompoundUseSiteInfo(diagnostics)) + Dim succeeded = method.CheckConstraints(Compilation.LanguageVersion, diagnosticLocation, diagnostics, template:=GetNewCompoundUseSiteInfo(diagnostics)) Debug.Assert(Not succeeded) Return End If diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Lookup.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Lookup.vb index 366b4ae2b1da4..cbeb2eb0d71ea 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Lookup.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Lookup.vb @@ -1217,7 +1217,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ' Process all methods from the same type together. Do ' Try to reduce this method and merge with the current result - Dim reduced As MethodSymbol = methods(i).ReduceExtensionMethod(container, proximity, useSiteInfo) + Dim reduced As MethodSymbol = methods(i).ReduceExtensionMethod(container, proximity, useSiteInfo, binder.Compilation.LanguageVersion) If reduced IsNot Nothing Then lookupResult.MergeOverloadedOrPrioritizedExtensionMethods(binder.CheckViability(reduced, arity, options, reduced.ContainingType, useSiteInfo)) diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb index f6da9ae8a394e..b3e96eea9d9d0 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb @@ -175,7 +175,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ' Interface IA(Of T As C) : End Interface ' Interface IB(Of T As C) : Inherits IA(Of T) : End Interface If checkConstraints AndAlso ShouldCheckConstraints Then - constructedType.CheckConstraintsForNonTuple(syntaxArguments, diagnostics, template:=GetNewCompoundUseSiteInfo(diagnostics)) + constructedType.CheckConstraintsForNonTuple(Compilation.LanguageVersion, syntaxArguments, diagnostics, template:=GetNewCompoundUseSiteInfo(diagnostics)) End If constructedType = DirectCast(TupleTypeSymbol.TransformToTupleIfCompatible(constructedType), NamedTypeSymbol) diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Utils.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Utils.vb index f674f21110d33..12e56274d263c 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Utils.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Utils.vb @@ -301,7 +301,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic If ShouldCheckConstraints Then Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - constructedType.CheckConstraints(diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=GetNewCompoundUseSiteInfo(diagBag)) + constructedType.CheckConstraints(Compilation.LanguageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=GetNewCompoundUseSiteInfo(diagBag)) If useSiteDiagnosticsBuilder IsNot Nothing Then diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder) diff --git a/src/Compilers/VisualBasic/Portable/Binding/BindingLocation.vb b/src/Compilers/VisualBasic/Portable/Binding/BindingLocation.vb index 64627343e1664..ee21c0c6e4394 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/BindingLocation.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/BindingLocation.vb @@ -21,8 +21,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic EventSignature FieldType HandlesClause + PropertyType PropertySignature PropertyAccessorSignature + EventType EventAccessorSignature End Enum diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index 51525fd134e32..cb0257ff9b975 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -2844,6 +2844,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic name) End Function + Protected Overrides Function CommonCreatePreprocessingSymbol(name As String) As IPreprocessingSymbol + Return New PreprocessingSymbol(name) + End Function + Protected Overrides Function CommonCreateArrayTypeSymbol(elementType As ITypeSymbol, rank As Integer, elementNullableAnnotation As NullableAnnotation) As IArrayTypeSymbol Return CreateArrayTypeSymbol(elementType.EnsureVbSymbolOrNothing(Of TypeSymbol)(NameOf(elementType)), rank) End Function diff --git a/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/VisualBasicSymbolMatcher.vb b/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/VisualBasicSymbolMatcher.vb index f14fa7854ca66..db9448ac0f6a1 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/VisualBasicSymbolMatcher.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/EditAndContinue/VisualBasicSymbolMatcher.vb @@ -478,6 +478,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Debug.Assert(type.AllowsRefLikeType = other.AllowsRefLikeType) Debug.Assert(type.HasReferenceTypeConstraint = other.HasReferenceTypeConstraint) Debug.Assert(type.ConstraintTypesNoUseSiteDiagnostics.Length = other.ConstraintTypesNoUseSiteDiagnostics.Length) + Debug.Assert(type.HasUnmanagedTypeConstraint = other.HasUnmanagedTypeConstraint) Return True End Function diff --git a/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb b/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb index 90324908c2390..48fe778a53989 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb @@ -1366,6 +1366,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERRID.ERR_DoNotUseRequiredMember, ERRID.ERR_UnsupportedRefReturningCallInWithStatement, ERRID.ERR_TypeReserved, + ERRID.ERR_UnmanagedConstraintNotSatisfied, ERRID.ERR_NextAvailable, ERRID.WRN_UseOfObsoleteSymbol2, ERRID.WRN_InvalidOverrideDueToTupleNames2, diff --git a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb index 337adb8b5246a..7a1f4efecbec3 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb @@ -1781,8 +1781,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERR_LockTypeUnsupported = 37329 ERR_InvalidVersionFormatDeterministic = 37330 ERR_TypeReserved = 37331 + ERR_UnmanagedConstraintNotSatisfied = 37332 - ERR_NextAvailable = 37332 + ERR_NextAvailable = 37333 '// WARNINGS BEGIN HERE WRN_UseOfObsoleteSymbol2 = 40000 @@ -2081,5 +2082,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic FEATURE_CommentsAfterLineContinuation FEATURE_InitOnlySettersUsage FEATURE_CallerArgumentExpression + FEATURE_UnmanagedConstraint End Enum End Namespace diff --git a/src/Compilers/VisualBasic/Portable/LanguageVersion.vb b/src/Compilers/VisualBasic/Portable/LanguageVersion.vb index 2c416182c3ac8..1a6aac70c9f81 100644 --- a/src/Compilers/VisualBasic/Portable/LanguageVersion.vb +++ b/src/Compilers/VisualBasic/Portable/LanguageVersion.vb @@ -21,6 +21,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic VisualBasic15_5 = 1505 VisualBasic16 = 1600 VisualBasic16_9 = 1609 + VisualBasic17_13 = 1713 Latest = Integer.MaxValue End Enum @@ -39,7 +40,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic LanguageVersion.VisualBasic15_3, LanguageVersion.VisualBasic15_5, LanguageVersion.VisualBasic16, - LanguageVersion.VisualBasic16_9 + LanguageVersion.VisualBasic16_9, + LanguageVersion.VisualBasic17_13 Return True End Select @@ -71,6 +73,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return "16" Case LanguageVersion.VisualBasic16_9 Return "16.9" + Case LanguageVersion.VisualBasic17_13 + Return "17.13" Case Else Throw ExceptionUtilities.UnexpectedValue(value) End Select @@ -87,7 +91,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Function MapSpecifiedToEffectiveVersion(version As LanguageVersion) As LanguageVersion Select Case version Case LanguageVersion.Latest - Return LanguageVersion.VisualBasic16_9 + Return LanguageVersion.VisualBasic17_13 Case LanguageVersion.Default Return LanguageVersion.VisualBasic16_9 Case Else @@ -97,7 +101,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Friend ReadOnly Property CurrentVersion As LanguageVersion Get - Return LanguageVersion.VisualBasic16_9 + Return LanguageVersion.VisualBasic17_13 End Get End Property @@ -128,6 +132,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return "16" Case LanguageVersion.VisualBasic16_9 Return "16.9" + Case LanguageVersion.VisualBasic17_13 + Return "17.13" Case LanguageVersion.Default Return "default" Case LanguageVersion.Latest @@ -167,6 +173,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic result = LanguageVersion.VisualBasic16 Case "16.9" result = LanguageVersion.VisualBasic16_9 + Case "17.13" + result = LanguageVersion.VisualBasic17_13 Case "default" result = LanguageVersion.Default Case "latest" diff --git a/src/Compilers/VisualBasic/Portable/Parser/Parser.vb b/src/Compilers/VisualBasic/Portable/Parser/Parser.vb index b9423962bd48f..2b7dc24d71d1f 100644 --- a/src/Compilers/VisualBasic/Portable/Parser/Parser.vb +++ b/src/Compilers/VisualBasic/Portable/Parser/Parser.vb @@ -6131,9 +6131,7 @@ checkNullable: Friend Shared Function CheckFeatureAvailability(diagnosticsOpt As DiagnosticBag, location As Location, languageVersion As LanguageVersion, feature As Feature) As Boolean If Not CheckFeatureAvailability(languageVersion, feature) Then If diagnosticsOpt IsNot Nothing Then - Dim featureName = ErrorFactory.ErrorInfo(feature.GetResourceId()) - Dim requiredVersion = New VisualBasicRequiredLanguageVersion(feature.GetLanguageVersion()) - diagnosticsOpt.Add(ERRID.ERR_LanguageVersion, location, languageVersion.GetErrorName(), featureName, requiredVersion) + diagnosticsOpt.Add(GetFeatureAvailabilityError(feature, languageVersion), location) End If Return False @@ -6141,6 +6139,14 @@ checkNullable: Return True End Function + Friend Shared Function GetFeatureAvailabilityError(feature As Feature, languageVersion As LanguageVersion) As DiagnosticInfo + Return ErrorFactory.ErrorInfo( + ERRID.ERR_LanguageVersion, + languageVersion.GetErrorName(), + ErrorFactory.ErrorInfo(feature.GetResourceId()), + New VisualBasicRequiredLanguageVersion(feature.GetLanguageVersion())) + End Function + Friend Shared Function CheckFeatureAvailability(diagnostics As BindingDiagnosticBag, location As Location, languageVersion As LanguageVersion, feature As Feature) As Boolean Return CheckFeatureAvailability(diagnostics.DiagnosticBag, location, languageVersion, feature) End Function diff --git a/src/Compilers/VisualBasic/Portable/Parser/ParserFeature.vb b/src/Compilers/VisualBasic/Portable/Parser/ParserFeature.vb index 71a47e3f057fd..9235e57dd1710 100644 --- a/src/Compilers/VisualBasic/Portable/Parser/ParserFeature.vb +++ b/src/Compilers/VisualBasic/Portable/Parser/ParserFeature.vb @@ -41,6 +41,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax UnconstrainedTypeParameterInConditional CommentsAfterLineContinuation InitOnlySettersUsage + UnmanagedConstraint End Enum Friend Module FeatureExtensions @@ -105,6 +106,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Case Feature.InitOnlySettersUsage Return LanguageVersion.VisualBasic16_9 + + Case Feature.UnmanagedConstraint + Return LanguageVersion.VisualBasic17_13 + Case Else Throw ExceptionUtilities.UnexpectedValue(feature) End Select @@ -178,6 +183,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax Return ERRID.FEATURE_CommentsAfterLineContinuation Case Feature.InitOnlySettersUsage Return ERRID.FEATURE_InitOnlySettersUsage + Case Feature.UnmanagedConstraint + Return ERRID.FEATURE_UnmanagedConstraint Case Else Throw ExceptionUtilities.UnexpectedValue(feature) End Select diff --git a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt index 8b137891791fe..360164d8fcc64 100644 --- a/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/VisualBasic/Portable/PublicAPI.Unshipped.txt @@ -1 +1 @@ - +Microsoft.CodeAnalysis.VisualBasic.LanguageVersion.VisualBasic17_13 = 1713 -> Microsoft.CodeAnalysis.VisualBasic.LanguageVersion diff --git a/src/Compilers/VisualBasic/Portable/Semantics/Conversions.vb b/src/Compilers/VisualBasic/Portable/Semantics/Conversions.vb index 7dd98a025230b..568685e8b4627 100644 --- a/src/Compilers/VisualBasic/Portable/Semantics/Conversions.vb +++ b/src/Compilers/VisualBasic/Portable/Semantics/Conversions.vb @@ -2032,6 +2032,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic While candidate IsNot Nothing If ConstraintsHelper.CheckConstraints(constructedSymbol:=Nothing, + LanguageVersion.Latest, ' Classifying conversions from/to type parameters. This is meaningful only when they and their constraints are declared in source substitution:=Nothing, typeParameter:=typeParam, typeArgument:=candidate, diff --git a/src/Compilers/VisualBasic/Portable/Semantics/OverloadResolution.vb b/src/Compilers/VisualBasic/Portable/Semantics/OverloadResolution.vb index ebc523567a7c2..6d69e5c4ce84c 100644 --- a/src/Compilers/VisualBasic/Portable/Semantics/OverloadResolution.vb +++ b/src/Compilers/VisualBasic/Portable/Semantics/OverloadResolution.vb @@ -2895,7 +2895,7 @@ Bailout: If method.IsGenericMethod Then Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - Dim satisfiedConstraints = method.CheckConstraints(diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=useSiteInfo) + Dim satisfiedConstraints = method.CheckConstraints(binder.Compilation.LanguageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=useSiteInfo) diagnosticsBuilder.Free() If useSiteDiagnosticsBuilder IsNot Nothing AndAlso useSiteDiagnosticsBuilder.Count > 0 Then diff --git a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.vb b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.vb index 4f872a3a02462..93acdd2c23223 100644 --- a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.vb +++ b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.vb @@ -199,7 +199,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic AddSpace() Case MethodKind.PropertyGet - If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then AddKeyword(SyntaxKind.FunctionKeyword) AddSpace() Else @@ -210,7 +210,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If Case MethodKind.PropertySet - If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then AddKeyword(SyntaxKind.SubKeyword) AddSpace() Else @@ -224,7 +224,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic MethodKind.EventRemove, MethodKind.EventRaise - If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then AddKeyword(SyntaxKind.SubKeyword) AddSpace() Else @@ -245,7 +245,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim tokenKind As SyntaxKind = TryGetConversionTokenKind(symbol) - If tokenKind = SyntaxKind.None OrElse Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If tokenKind = SyntaxKind.None OrElse Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then AddKeyword(SyntaxKind.FunctionKeyword) AddSpace() Else @@ -259,7 +259,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Case MethodKind.UserDefinedOperator, MethodKind.BuiltinOperator Dim tokenKind As SyntaxKind = TryGetOperatorTokenKind(symbol) - If tokenKind = SyntaxKind.None OrElse Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If tokenKind = SyntaxKind.None OrElse Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then AddKeyword(SyntaxKind.FunctionKeyword) AddSpace() Else @@ -322,7 +322,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic MethodKind.EventRemove, MethodKind.EventRaise - If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then Builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, symbol.Name, visitedParents)) Else Dim associatedPropertyOrEvent = symbol.AssociatedSymbol @@ -336,7 +336,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If Case MethodKind.Constructor, MethodKind.StaticConstructor - If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then Builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, symbol.Name, visitedParents)) Else AddKeyword(SyntaxKind.NewKeyword) @@ -346,7 +346,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim tokenKind As SyntaxKind = TryGetOperatorTokenKind(symbol) - If tokenKind = SyntaxKind.None OrElse Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If tokenKind = SyntaxKind.None OrElse Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then Builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, symbol.Name, visitedParents)) ElseIf SyntaxFacts.IsKeywordKind(tokenKind) Then ' Prefer to add the operator token as a keyword if it considered both a keyword and an operator. @@ -361,7 +361,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Case MethodKind.Conversion Dim tokenKind As SyntaxKind = TryGetConversionTokenKind(symbol) - If tokenKind = SyntaxKind.None OrElse Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) Then + If tokenKind = SyntaxKind.None OrElse Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) Then Builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, symbol.Name, visitedParents)) Else AddKeyword(SyntaxKind.CTypeKeyword) diff --git a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.vb b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.vb index 56b77ffe546ef..f970dff1a2df3 100644 --- a/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.vb +++ b/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.vb @@ -315,6 +315,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If End Sub + Friend Overrides Sub VisitPreprocessing(symbol As IPreprocessingSymbol) + Dim part = New SymbolDisplayPart(SymbolDisplayPartKind.Text, symbol, symbol.Name) + builder.Add(part) + End Sub + Protected Overrides Sub AddSpace() Builder.Add(CreatePart(SymbolDisplayPartKind.Space, Nothing, " ", False)) End Sub diff --git a/src/Compilers/VisualBasic/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousTypeOrDelegateTypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousTypeOrDelegateTypeParameterSymbol.vb index f8571dbac4861..9d48c9b4c2517 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousTypeOrDelegateTypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousTypeOrDelegateTypeParameterSymbol.vb @@ -42,6 +42,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return False + End Get + End Property + Public Overrides ReadOnly Property Locations As ImmutableArray(Of Location) Get Return ImmutableArray(Of Location).Empty diff --git a/src/Compilers/VisualBasic/Portable/Symbols/ArrayTypeSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/ArrayTypeSymbol.vb index baffd44f6e146..e209b6032c0a8 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/ArrayTypeSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/ArrayTypeSymbol.vb @@ -364,6 +364,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Friend MustOverride Function WithElementType(elementType As TypeSymbol) As ArrayTypeSymbol + Friend Overrides Function GetManagedKind(ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ManagedKind + Return ManagedKind.Managed + End Function + #Region "Use-Site Diagnostics" Friend Overrides Function GetUseSiteInfo() As UseSiteInfo(Of AssemblySymbol) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/ConstraintsHelper.vb b/src/Compilers/VisualBasic/Portable/Symbols/ConstraintsHelper.vb index d7a8f078fc96a..129ee7d783771 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/ConstraintsHelper.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/ConstraintsHelper.vb @@ -383,12 +383,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Sub CheckAllConstraints( type As TypeSymbol, + languageVersion As LanguageVersion, loc As Location, diagnostics As BindingDiagnosticBag, template As CompoundUseSiteInfo(Of AssemblySymbol)) Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - type.CheckAllConstraints(diagnosticsBuilder, useSiteDiagnosticsBuilder, template) + type.CheckAllConstraints(languageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) If useSiteDiagnosticsBuilder IsNot Nothing Then diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder) @@ -409,6 +410,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Sub CheckAllConstraints( type As TypeSymbol, + languageVersion As LanguageVersion, diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), <[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), template As CompoundUseSiteInfo(Of AssemblySymbol)) @@ -416,6 +418,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols diagnostics.diagnosticsBuilder = diagnosticsBuilder diagnostics.useSiteDiagnosticsBuilder = useSiteDiagnosticsBuilder diagnostics.template = template + diagnostics.languageVersion = languageVersion type.VisitType(s_checkConstraintsSingleTypeFunc, diagnostics) @@ -426,13 +429,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) Public useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) Public template As CompoundUseSiteInfo(Of AssemblySymbol) + Public languageVersion As LanguageVersion End Class Private ReadOnly s_checkConstraintsSingleTypeFunc As Func(Of TypeSymbol, CheckConstraintsDiagnosticsBuilders, Boolean) = AddressOf CheckConstraintsSingleType Private Function CheckConstraintsSingleType(type As TypeSymbol, diagnostics As CheckConstraintsDiagnosticsBuilders) As Boolean If type.Kind = SymbolKind.NamedType Then - DirectCast(type, NamedTypeSymbol).CheckConstraints(diagnostics.diagnosticsBuilder, diagnostics.useSiteDiagnosticsBuilder, diagnostics.template) + DirectCast(type, NamedTypeSymbol).CheckConstraints( + diagnostics.languageVersion, diagnostics.diagnosticsBuilder, diagnostics.useSiteDiagnosticsBuilder, diagnostics.template) End If Return False ' continue walking types End Function @@ -460,7 +465,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim offset As Integer = 0 For Each underlyingTuple In underlyingTupleTypeChain Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - CheckTypeConstraints(underlyingTuple, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) + CheckTypeConstraints(underlyingTuple, + DirectCast(syntaxNode.SyntaxTree.Options, VisualBasicParseOptions).LanguageVersion, + diagnosticsBuilder, useSiteDiagnosticsBuilder, template) If useSiteDiagnosticsBuilder IsNot Nothing Then diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder) @@ -487,6 +494,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Function CheckConstraintsForNonTuple( type As NamedTypeSymbol, + languageVersion As LanguageVersion, typeArgumentsSyntax As SeparatedSyntaxList(Of TypeSyntax), diagnostics As BindingDiagnosticBag, template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean @@ -498,7 +506,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - Dim result = CheckTypeConstraints(type, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) + Dim result = CheckTypeConstraints(type, languageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) If useSiteDiagnosticsBuilder IsNot Nothing Then diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder) @@ -517,6 +525,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Function CheckConstraints( type As NamedTypeSymbol, + languageVersion As LanguageVersion, diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), <[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean @@ -529,12 +538,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols If Not RequiresChecking(type) Then Return True End If - Return CheckTypeConstraints(type, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) + Return CheckTypeConstraints(type, languageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) End Function Public Function CheckConstraints( method As MethodSymbol, + languageVersion As LanguageVersion, diagnosticLocation As Location, diagnostics As BindingDiagnosticBag, template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean @@ -544,7 +554,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - Dim result = CheckMethodConstraints(method, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) + Dim result = CheckMethodConstraints(method, languageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) If useSiteDiagnosticsBuilder IsNot Nothing Then diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder) @@ -561,31 +571,34 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Function CheckConstraints( method As MethodSymbol, + languageVersion As LanguageVersion, diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), <[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean If Not RequiresChecking(method) Then Return True End If - Return CheckMethodConstraints(method, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) + Return CheckMethodConstraints(method, languageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) End Function Private Function CheckTypeConstraints( type As NamedTypeSymbol, + languageVersion As LanguageVersion, diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), <[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean Dim substitution = type.TypeSubstitution - Return CheckConstraints(type, substitution, type.OriginalDefinition.TypeParameters, type.TypeArgumentsNoUseSiteDiagnostics, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) + Return CheckConstraints(type, languageVersion, substitution, type.OriginalDefinition.TypeParameters, type.TypeArgumentsNoUseSiteDiagnostics, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) End Function Private Function CheckMethodConstraints( method As MethodSymbol, + languageVersion As LanguageVersion, diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), <[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo), template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean Dim substitution = DirectCast(method, SubstitutedMethodSymbol).TypeSubstitution - Return CheckConstraints(method, substitution, method.OriginalDefinition.TypeParameters, method.TypeArguments, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) + Return CheckConstraints(method, languageVersion, substitution, method.OriginalDefinition.TypeParameters, method.TypeArguments, diagnosticsBuilder, useSiteDiagnosticsBuilder, template) End Function ''' @@ -599,6 +612,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Function CheckConstraints( constructedSymbol As Symbol, + languageVersion As LanguageVersion, substitution As TypeSubstitution, typeParameters As ImmutableArray(Of TypeParameterSymbol), typeArguments As ImmutableArray(Of TypeSymbol), @@ -614,7 +628,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim typeArgument = typeArguments(i) Dim typeParameter = typeParameters(i) Dim useSiteInfo As New CompoundUseSiteInfo(Of AssemblySymbol)(template) - If Not CheckConstraints(constructedSymbol, substitution, typeParameter, typeArgument, diagnosticsBuilder, useSiteInfo) Then + If Not CheckConstraints(constructedSymbol, languageVersion, substitution, typeParameter, typeArgument, diagnosticsBuilder, useSiteInfo) Then succeeded = False End If @@ -628,6 +642,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Function CheckConstraints( constructedSymbol As Symbol, + languageVersion As LanguageVersion, substitution As TypeSubstitution, typeParameter As TypeParameterSymbol, typeArgument As TypeSymbol, @@ -659,6 +674,24 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols succeeded = False End If + If typeParameter.HasUnmanagedTypeConstraint Then + + If Not InternalSyntax.Parser.CheckFeatureAvailability(languageVersion, InternalSyntax.Feature.UnmanagedConstraint) Then + useSiteInfo.AddDiagnosticInfo(InternalSyntax.Parser.GetFeatureAvailabilityError(InternalSyntax.Feature.UnmanagedConstraint, languageVersion)) + succeeded = False + End If + + Dim managedKind = typeArgument.GetManagedKind(useSiteInfo) + + If managedKind = ManagedKind.Managed Then + If diagnosticsBuilder IsNot Nothing Then + diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter, ErrorFactory.ErrorInfo(ERRID.ERR_UnmanagedConstraintNotSatisfied, typeArgument, typeParameter))) + End If + + succeeded = False + End If + End If + If typeParameter.HasValueTypeConstraint AndAlso Not SatisfiesValueTypeConstraint(constructedSymbol, typeParameter, typeArgument, diagnosticsBuilder, useSiteInfo) Then succeeded = False End If diff --git a/src/Compilers/VisualBasic/Portable/Symbols/IndexedTypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/IndexedTypeParameterSymbol.vb index dca460d7f317d..d327dc3b1a378 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/IndexedTypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/IndexedTypeParameterSymbol.vb @@ -140,6 +140,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return False + End Get + End Property + Public Overrides ReadOnly Property ContainingSymbol As Symbol Get Return Nothing diff --git a/src/Compilers/VisualBasic/Portable/Symbols/InstanceErrorTypeSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/InstanceErrorTypeSymbol.vb index dd6486d1d2839..37297227743c9 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/InstanceErrorTypeSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/InstanceErrorTypeSymbol.vb @@ -205,6 +205,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return False + End Get + End Property + Friend Overrides Function GetConstraints() As ImmutableArray(Of TypeParameterConstraint) Return ImmutableArray(Of TypeParameterConstraint).Empty End Function diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.vb index 6b033ecd2b07c..e1d387a2b4924 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PETypeParameterSymbol.vb @@ -12,6 +12,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports System.Reflection Imports System.Reflection.Metadata.Ecma335 +Imports System.Runtime.InteropServices Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE @@ -34,6 +35,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Private _lazyConstraintTypes As ImmutableArray(Of TypeSymbol) + ''' + ''' Actually stores + ''' + Private _lazyHasIsUnmanagedConstraint As Byte + ''' ''' First error calculating bounds. ''' @@ -149,9 +155,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE End Get End Property - Private Function GetDeclaredConstraints() As ImmutableArray(Of TypeParameterConstraint) + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + EnsureAllConstraintsAreResolved() + Return CType(Volatile.Read(_lazyHasIsUnmanagedConstraint), ThreeState).Value() + End Get + End Property + + Private Function GetDeclaredConstraints( ByRef hasUnmanagedModreqPattern As Boolean) As ImmutableArray(Of TypeParameterConstraint) Dim constraintsBuilder = ArrayBuilder(Of TypeParameterConstraint).GetInstance() + hasUnmanagedModreqPattern = False + If HasConstructorConstraint Then constraintsBuilder.Add(New TypeParameterConstraint(TypeParameterConstraintKind.Constructor, Nothing)) End If @@ -194,9 +209,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE End If For Each constraintHandle In constraints - Dim constraint = metadataReader.GetGenericParameterConstraint(constraintHandle) - Dim constraintTypeHandle = constraint.Type - Dim typeSymbol As TypeSymbol = tokenDecoder.GetTypeOfToken(constraintTypeHandle) + Dim typeSymbol As TypeSymbol = GetConstraintType(metadataReader, tokenDecoder, constraintHandle, hasUnmanagedModreqPattern) ' Drop 'System.ValueType' constraint type if the 'valuetype' constraint was also specified. If ((_flags And GenericParameterAttributes.NotNullableValueTypeConstraint) <> 0) AndAlso @@ -212,9 +225,50 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Next End If + ' - presence of unmanaged pattern has to be matched with `valuetype` + ' - IsUnmanagedAttribute is allowed if there is an unmanaged pattern + If (hasUnmanagedModreqPattern AndAlso (_flags And GenericParameterAttributes.NotNullableValueTypeConstraint) = 0) OrElse + hasUnmanagedModreqPattern <> moduleSymbol.Module.HasIsUnmanagedAttribute(_handle) Then + ' we do not recognize these combinations as "unmanaged" + hasUnmanagedModreqPattern = False + _lazyCachedBoundsUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency:=Nothing, New UseSiteInfo(Of AssemblySymbol)(ErrorFactory.ErrorInfo(ERRID.ERR_UnsupportedType1, Me))) + End If + Return constraintsBuilder.ToImmutableAndFree() End Function + Private Shared Function GetConstraintType( + metadataReader As MetadataReader, + tokenDecoder As MetadataDecoder, + constraintHandle As GenericParameterConstraintHandle, + ByRef hasUnmanagedModreqPattern As Boolean + ) As TypeSymbol + + Dim constraint = metadataReader.GetGenericParameterConstraint(constraintHandle) + Dim modifiers As ImmutableArray(Of ModifierInfo(Of TypeSymbol)) = Nothing + Dim typeSymbol = tokenDecoder.DecodeGenericParameterConstraint(constraint.Type, modifiers) + + If Not modifiers.IsDefaultOrEmpty AndAlso modifiers.Length > 1 Then + typeSymbol = New UnsupportedMetadataTypeSymbol() + ElseIf typeSymbol.SpecialType = SpecialType.System_ValueType Then + ' recognize "(class [mscorlib]System.ValueType modreq([mscorlib]System.Runtime.InteropServices.UnmanagedType" pattern as "unmanaged" + If Not modifiers.IsDefaultOrEmpty Then + Dim m As ModifierInfo(Of TypeSymbol) = modifiers.Single() + If Not m.IsOptional AndAlso m.Modifier.IsWellKnownTypeUnmanagedType() Then + hasUnmanagedModreqPattern = True + Else + ' Any other modifiers, optional or not, are not allowed + typeSymbol = New UnsupportedMetadataTypeSymbol() + End If + End If + ElseIf Not modifiers.IsDefaultOrEmpty Then + ' Any other modifiers, optional or not, are not allowed + typeSymbol = New UnsupportedMetadataTypeSymbol() + End If + + Return typeSymbol + End Function + Public Overrides ReadOnly Property Locations As ImmutableArray(Of Location) Get Return _containingSymbol.Locations @@ -273,6 +327,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE If _lazyConstraintTypes.IsDefault Then Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim inherited = (_containingSymbol.Kind = SymbolKind.Method) AndAlso DirectCast(_containingSymbol, MethodSymbol).IsOverrides + Dim hasUnmanagedModreqPattern As Boolean = False ' Check direct constraints on the type parameter to generate any use-site errors ' (for example, the cycle in ".class public A<(!T)T>"). It's necessary to check for such @@ -284,7 +339,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE ' which cannot be satisfied if A and B are from different hierarchies.) It also isn't ' necessary to report redundant constraints since redundant constraints are still ' valid. Redundant constraints are dropped silently. - Dim constraints = Me.RemoveDirectConstraintConflicts(GetDeclaredConstraints(), inProgress.Prepend(Me), DirectConstraintConflictKind.None, diagnosticsBuilder) + Dim constraints = Me.RemoveDirectConstraintConflicts(GetDeclaredConstraints(hasUnmanagedModreqPattern), inProgress.Prepend(Me), DirectConstraintConflictKind.None, diagnosticsBuilder) Dim primaryDependency As AssemblySymbol = Me.PrimaryDependency Dim useSiteInfo As New UseSiteInfo(Of AssemblySymbol)(primaryDependency) @@ -299,6 +354,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE diagnosticsBuilder.Free() _lazyCachedBoundsUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency, useSiteInfo) + _lazyHasIsUnmanagedConstraint = hasUnmanagedModreqPattern.ToThreeState() + + ' Note, we are relying on the fact that _lazyConstraintTypes is initialized last, and + ' we depend on the memory barrier from this interlocked operation to prevent write reordering ImmutableInterlocked.InterlockedInitialize(_lazyConstraintTypes, GetConstraintTypesOnly(constraints)) End If diff --git a/src/Compilers/VisualBasic/Portable/Symbols/MethodSignatureComparer.vb b/src/Compilers/VisualBasic/Portable/Symbols/MethodSignatureComparer.vb index 39e5fbc32563e..c395012933648 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/MethodSignatureComparer.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/MethodSignatureComparer.vb @@ -864,6 +864,7 @@ Done: (typeParameter1.HasReferenceTypeConstraint <> typeParameter2.HasReferenceTypeConstraint) OrElse (typeParameter1.HasValueTypeConstraint <> typeParameter2.HasValueTypeConstraint) OrElse (typeParameter1.AllowsRefLikeType <> typeParameter2.AllowsRefLikeType) OrElse + (typeParameter1.HasUnmanagedTypeConstraint <> typeParameter2.HasUnmanagedTypeConstraint) OrElse (typeParameter1.Variance <> typeParameter2.Variance) Then Return False End If diff --git a/src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb index 5e3893146718c..3101385966ebc 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb @@ -784,16 +784,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ''' Name lookup should use this method in order to capture proximity, which affects ''' overload resolution. ''' - Friend Function ReduceExtensionMethod(instanceType As TypeSymbol, proximity As Integer, ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As MethodSymbol - Return ReducedExtensionMethodSymbol.Create(instanceType, Me, proximity, useSiteInfo) + Friend Function ReduceExtensionMethod(instanceType As TypeSymbol, proximity As Integer, ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol), languageVersion As LanguageVersion) As MethodSymbol + Return ReducedExtensionMethodSymbol.Create(instanceType, Me, proximity, useSiteInfo, languageVersion) End Function ''' ''' If this is an extension method that can be applied to a instance of the given type, ''' returns the reduced method symbol thus formed. Otherwise, returns Nothing. ''' - Public Function ReduceExtensionMethod(instanceType As TypeSymbol, ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As MethodSymbol - Return ReduceExtensionMethod(instanceType, proximity:=0, useSiteInfo) + Public Function ReduceExtensionMethod(instanceType As TypeSymbol, ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol), languageVersion As LanguageVersion) As MethodSymbol + Return ReduceExtensionMethod(instanceType, proximity:=0, useSiteInfo, languageVersion) End Function ''' @@ -965,7 +965,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Throw New ArgumentNullException(NameOf(receiverType)) End If - Return Me.ReduceExtensionMethod(receiverType.EnsureVbSymbolOrNothing(Of TypeSymbol)(NameOf(receiverType)), CompoundUseSiteInfo(Of AssemblySymbol).Discarded) + Return Me.ReduceExtensionMethod(receiverType.EnsureVbSymbolOrNothing(Of TypeSymbol)(NameOf(receiverType)), CompoundUseSiteInfo(Of AssemblySymbol).Discarded, LanguageVersion.Latest) End Function Private ReadOnly Property IMethodSymbol_Parameters As ImmutableArray(Of IParameterSymbol) Implements IMethodSymbol.Parameters diff --git a/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb index 03f4062b917df..b983ef0e1e1e1 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb @@ -936,7 +936,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ''' ''' True if and only if this type or some containing type has type parameters. ''' - Public ReadOnly Property IsGenericType As Boolean Implements INamedTypeSymbol.IsGenericType + Public ReadOnly Property IsGenericType As Boolean Implements INamedTypeSymbol.IsGenericType, INamedTypeSymbolInternal.IsGenericType Get Dim p As NamedTypeSymbol = Me Do While p IsNot Nothing @@ -1195,6 +1195,121 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return True End Function + Friend Overrides Function GetManagedKind(ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ManagedKind + Return GetManagedKind(Me, useSiteInfo) + End Function + + ''' + ''' is simple for most named types: + ''' enums are not managed; + ''' non-enum, non-struct named types are managed; + ''' type parameters are managed unless an 'unmanaged' constraint is present; + ''' all special types have spec'd values (basically, (non-string) primitives) are not managed; + ''' + ''' Only structs are complicated, because the definition is recursive. A struct type is managed + ''' if one of its instance fields is managed or a ref field. Unfortunately, this can result in infinite recursion. + ''' If the closure is finite, and we don't find anything definitely managed, then we return true. + ''' If the closure is infinite, we disregard all but a representative of any expanding cycle. + ''' + ''' Intuitively, this will only return true if there's a specific type we can point to that is would + ''' be managed even if it had no fields. e.g. struct S { S s; } is not managed, but struct S { S s; object o; } + ''' is because we can point to object. + ''' + Private Overloads Shared Function GetManagedKind(type As NamedTypeSymbol, ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ManagedKind + type = DirectCast(type.GetTupleUnderlyingTypeOrSelf(), NamedTypeSymbol) + + ' The code below is a clone of BaseTypeAnalysis.GetManagedKind from C#. It should be kept in sync going forward + Dim managedInfo = INamedTypeSymbolInternal.Helpers.IsManagedTypeHelper(type) + Dim definitelyManaged = (managedInfo.isManaged = ThreeState.True) + + If managedInfo.isManaged = ThreeState.Unknown Then + ' Otherwise, we have to build and inspect the closure of depended-upon types. + Dim hs = PooledHashSet(Of Symbol).GetInstance() + Dim result = DependsOnDefinitelyManagedType(type, hs, useSiteInfo) + definitelyManaged = result.definitelyManaged + managedInfo.hasGenerics = managedInfo.hasGenerics OrElse result.hasGenerics + hs.Free() + End If + + If definitelyManaged Then + Return ManagedKind.Managed + ElseIf managedInfo.hasGenerics Then + Return ManagedKind.UnmanagedWithGenerics + Else + Return ManagedKind.Unmanaged + End If + End Function + + Private Shared Function DependsOnDefinitelyManagedType( + type As NamedTypeSymbol, + partialClosure As HashSet(Of Symbol), + ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol) + ) As (definitelyManaged As Boolean, hasGenerics As Boolean) + + Debug.Assert(type IsNot Nothing) + + Dim hasGenerics = False + + If partialClosure.Add(type) Then + + For Each member In type.GetMembersUnordered() + + Dim field = TryCast(member, FieldSymbol) + + ' Only instance fields (including field-like events) affect the outcome. + If field Is Nothing OrElse field.IsShared Then + Continue For + End If + + useSiteInfo.Add(field.GetUseSiteInfo()) + Dim fieldType As TypeSymbol = field.Type.GetTupleUnderlyingTypeOrSelf() + + ' A ref struct which has a ref field is never considered unmanaged + ' but we cannot represent ref fields in VB + ' The previous line should collect an error about the fact + + Select Case fieldType.TypeKind + Case TypeKind.Pointer, TypeKind.FunctionPointer + ' pointers are unmanaged + ExceptionUtilities.UnexpectedValue(fieldType.TypeKind) + Continue For + End Select + + Dim fieldNamedType = TryCast(fieldType, NamedTypeSymbol) + If fieldNamedType Is Nothing Then + If fieldType.GetManagedKind(useSiteInfo) = ManagedKind.Managed Then + Return (True, hasGenerics) + End If + Else + Dim result = INamedTypeSymbolInternal.Helpers.IsManagedTypeHelper(fieldNamedType) + hasGenerics = hasGenerics OrElse result.hasGenerics + ' NOTE: don't use GetManagedKind on a NamedTypeSymbol - that could lead + ' to infinite recursion. + Select Case result.isManaged + Case ThreeState.True + Return (True, hasGenerics) + + Case ThreeState.False + Continue For + + Case ThreeState.Unknown + If Not fieldNamedType.OriginalDefinition.KnownCircularStruct Then + Dim dependsInfo = DependsOnDefinitelyManagedType(fieldNamedType, partialClosure, useSiteInfo) + hasGenerics = hasGenerics OrElse dependsInfo.hasGenerics + If dependsInfo.definitelyManaged Then + Return (True, hasGenerics) + End If + End If + + Continue For + End Select + End If + Next + End If + + Return (False, hasGenerics) + End Function + #Region "INamedTypeSymbol" Private ReadOnly Property INamedTypeSymbol_Arity As Integer Implements INamedTypeSymbol.Arity diff --git a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb index 20d7d30a8d5ec..84d209729623e 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/PreprocessingSymbol.vb @@ -109,7 +109,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Function Public Overloads Overrides Sub Accept(visitor As SymbolVisitor) - Throw New NotSupportedException() + visitor.VisitPreprocessing(Me) End Sub Public Overloads Overrides Sub Accept(visitor As VisualBasicSymbolVisitor) @@ -117,11 +117,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Sub Public Overloads Overrides Function Accept(Of TResult)(visitor As SymbolVisitor(Of TResult)) As TResult - Throw New NotSupportedException() + Return visitor.VisitPreprocessing(Me) End Function Public Overrides Function Accept(Of TArgument, TResult)(visitor As SymbolVisitor(Of TArgument, TResult), argument As TArgument) As TResult - Throw New NotSupportedException() + Return visitor.VisitPreprocessing(Me, argument) End Function Public Overloads Overrides Function Accept(Of TResult)(visitor As VisualBasicSymbolVisitor(Of TResult)) As TResult diff --git a/src/Compilers/VisualBasic/Portable/Symbols/ReducedExtensionMethodSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/ReducedExtensionMethodSymbol.vb index 7cad34b2202d4..bc1475beaed91 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/ReducedExtensionMethodSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/ReducedExtensionMethodSymbol.vb @@ -32,7 +32,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ''' If this is an extension method that can be applied to an instance of the given type, ''' returns the curried method symbol thus formed. Otherwise, returns Nothing. ''' - Public Shared Function Create(instanceType As TypeSymbol, possiblyExtensionMethod As MethodSymbol, proximity As Integer, ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As MethodSymbol + Public Shared Function Create( + instanceType As TypeSymbol, + possiblyExtensionMethod As MethodSymbol, + proximity As Integer, + ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol), + languageVersion As LanguageVersion + ) As MethodSymbol + Debug.Assert(instanceType IsNot Nothing) Debug.Assert(possiblyExtensionMethod IsNot Nothing) Debug.Assert(proximity >= 0) @@ -140,7 +147,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ' Check constraints. Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - success = possiblyExtensionMethod.CheckConstraints(partialSubstitution, + success = possiblyExtensionMethod.CheckConstraints(languageVersion, + partialSubstitution, typeParametersToFixArray, fixWithArray, diagnosticsBuilder, @@ -721,6 +729,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return _curriedFromTypeParameter.HasUnmanagedTypeConstraint + End Get + End Property + Public Overrides ReadOnly Property ContainingSymbol As Symbol Get Return _curriedMethod diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingTypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingTypeParameterSymbol.vb index b814ac65952a1..4084c4e823693 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingTypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingTypeParameterSymbol.vb @@ -80,6 +80,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Retargeting End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return _underlyingTypeParameter.HasUnmanagedTypeConstraint + End Get + End Property + Public Overrides ReadOnly Property HasConstructorConstraint As Boolean Get Return _underlyingTypeParameter.HasConstructorConstraint diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/CrefTypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/CrefTypeParameterSymbol.vb index dd14245f73a15..ba0c92ccaf095 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/CrefTypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/CrefTypeParameterSymbol.vb @@ -79,6 +79,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return False + End Get + End Property + Friend Overrides Function GetConstraints() As ImmutableArray(Of TypeParameterConstraint) Return ImmutableArray(Of TypeParameterConstraint).Empty End Function diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceEventSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceEventSymbol.vb index 5513ecf608895..8cf47f22d2330 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceEventSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceEventSymbol.vb @@ -40,6 +40,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols IsDelegateFromImplements = &H2 ' Bit value valid once m_lazyType is assigned. ReportedExplicitImplementationDiagnostics = &H4 SymbolDeclaredEvent = &H8 ' Bit value for generating SymbolDeclaredEvent + TypeConstraintsChecked = &H10 End Enum Private _lazyType As TypeSymbol @@ -150,7 +151,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Sub Private Function ComputeType(diagnostics As BindingDiagnosticBag, ByRef isTypeInferred As Boolean, ByRef isDelegateFromImplements As Boolean) As TypeSymbol - Dim binder = CreateBinderForTypeDeclaration() + Dim binder = BinderBuilder.CreateBinderForType(ContainingSourceModule, _syntaxRef.SyntaxTree, _containingType) + binder = New LocationSpecificBinder(BindingLocation.EventType, Me, binder) Dim syntax = DirectCast(_syntaxRef.GetSyntax(), EventStatementSyntax) isTypeInferred = False @@ -744,10 +746,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Friend Overrides Sub GenerateDeclarationErrors(cancellationToken As CancellationToken) MyBase.GenerateDeclarationErrors(cancellationToken) - Dim unusedType = Me.Type + Dim type = Me.Type Dim unusedImplementations = Me.ExplicitInterfaceImplementations Me.CheckExplicitImplementationTypes() + If (_lazyState And StateFlags.TypeConstraintsChecked) = 0 Then + Dim sourceModule = DirectCast(Me.ContainingModule, SourceModuleSymbol) + Dim diagnostics = BindingDiagnosticBag.GetInstance() + type.CheckAllConstraints(DeclaringCompilation.LanguageVersion, + Locations(0), diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, sourceModule.ContainingAssembly)) + sourceModule.AtomicSetFlagAndStoreDiagnostics(_lazyState, StateFlags.TypeConstraintsChecked, 0, diagnostics) + diagnostics.Free() + End If + If DeclaringCompilation.EventQueue IsNot Nothing Then Me.ContainingSourceModule.AtomicSetFlagAndRaiseSymbolDeclaredEvent(_lazyState, StateFlags.SymbolDeclaredEvent, 0, Me) End If diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFieldSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFieldSymbol.vb index 2556c6218da54..9e7528edde223 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFieldSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFieldSymbol.vb @@ -32,8 +32,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Private _lazyExpandedDocComment As String Private _lazyCustomAttributesBag As CustomAttributesBag(Of VisualBasicAttributeData) - ' Set to 1 when the compilation event has been produced - Private _eventProduced As Integer + ''' + ''' See + ''' + Protected _lazyState As Integer + + + Protected Enum StateFlags As Integer + TypeConstraintsChecked = &H1 + + EventProduced = &H2 + End Enum Protected Sub New(container As SourceMemberContainerTypeSymbol, syntaxRef As SyntaxReference, @@ -51,15 +60,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols m_memberFlags = memberFlags End Sub - Friend Overrides Sub GenerateDeclarationErrors(cancellationToken As CancellationToken) + Protected Overridable Sub GenerateDeclarationErrorsImpl(cancellationToken As CancellationToken) MyBase.GenerateDeclarationErrors(cancellationToken) Dim unusedType = Me.Type GetConstantValue(ConstantFieldsInProgress.Empty) + End Sub + + Friend NotOverridable Overrides Sub GenerateDeclarationErrors(cancellationToken As CancellationToken) + GenerateDeclarationErrorsImpl(cancellationToken) ' We want declaration events to be last, after all compilation analysis is done, so we produce them here Dim sourceModule = DirectCast(Me.ContainingModule, SourceModuleSymbol) - If Interlocked.CompareExchange(_eventProduced, 1, 0) = 0 AndAlso Not Me.IsImplicitlyDeclared Then + If ThreadSafeFlagOperations.Set(_lazyState, StateFlags.EventProduced) AndAlso Not Me.IsImplicitlyDeclared Then sourceModule.DeclaringCompilation.SymbolDeclaredEvent(Me) End If End Sub diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFile.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFile.vb index 98553d475e29f..d811f22c45274 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFile.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFile.vb @@ -360,7 +360,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim type = TryCast(namespaceOrType, TypeSymbol) If type IsNot Nothing Then clauseDiagnostics.Clear() - type.CheckAllConstraints(location, clauseDiagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, compilation.Assembly)) + type.CheckAllConstraints( + compilation.LanguageVersion, + location, clauseDiagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, compilation.Assembly)) diagnostics.AddRange(clauseDiagnostics.DiagnosticBag) If VisualBasicCompilation.ReportUnusedImportsInTree(location.PossiblyEmbeddedOrMySourceTree) Then diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMemberContainerTypeSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMemberContainerTypeSymbol.vb index 867e7eabf15a8..5119595df0745 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMemberContainerTypeSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMemberContainerTypeSymbol.vb @@ -2022,16 +2022,23 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ''' Set of processed structure types Public ReadOnly ProcessedTypes As HashSet(Of NamedTypeSymbol) + Public ReadOnly TypesWithCycle As HashSet(Of NamedTypeSymbol) + ''' Queue element structure Public Structure QueueElement Public ReadOnly Type As NamedTypeSymbol - Public ReadOnly Path As ConsList(Of FieldSymbol) + Public ReadOnly FieldPath As ConsList(Of FieldSymbol) + Public ReadOnly ContainingDefinitionsPath As ConsList(Of NamedTypeSymbol) + Public ReadOnly Report As Boolean - Public Sub New(type As NamedTypeSymbol, path As ConsList(Of FieldSymbol)) + Public Sub New(type As NamedTypeSymbol, fieldPath As ConsList(Of FieldSymbol), containingDefinitionsPath As ConsList(Of NamedTypeSymbol), report As Boolean) Debug.Assert(type IsNot Nothing) - Debug.Assert(path IsNot Nothing) + Debug.Assert(fieldPath IsNot Nothing) + Debug.Assert(containingDefinitionsPath IsNot Nothing) Me.Type = type - Me.Path = path + Me.FieldPath = fieldPath + Me.ContainingDefinitionsPath = containingDefinitionsPath + Me.Report = report End Sub End Structure @@ -2040,6 +2047,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Private Sub New() ProcessedTypes = New HashSet(Of NamedTypeSymbol)() + TypesWithCycle = New HashSet(Of NamedTypeSymbol)() Queue = New Queue(Of QueueElement) End Sub @@ -2050,6 +2058,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Sub Free() Me.Queue.Clear() Me.ProcessedTypes.Clear() + Me.TypesWithCycle.Clear() s_pool.Free(Me) End Sub @@ -2091,7 +2100,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ' Allocate data set Dim data = StructureCircularityDetectionDataSet.GetInstance() - data.Queue.Enqueue(New StructureCircularityDetectionDataSet.QueueElement(Me, ConsList(Of FieldSymbol).Empty)) + data.Queue.Enqueue(New StructureCircularityDetectionDataSet.QueueElement(Me, ConsList(Of FieldSymbol).Empty, ConsList(Of NamedTypeSymbol).Empty.Prepend(Me), report:=True)) Dim hasCycle = False @@ -2104,6 +2113,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Continue While End If + If data.TypesWithCycle.Contains(current.Type.OriginalDefinition) Then + Continue While + End If + Dim cycleReportedForCurrentType As Boolean = False ' iterate over non-static fields of structure data type @@ -2121,13 +2134,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Continue For End If - If fieldType.OriginalDefinition.Equals(Me) Then + If current.ContainingDefinitionsPath.ContainsReference(fieldType.OriginalDefinition) Then ' a cycle detected - If Not cycleReportedForCurrentType Then + data.TypesWithCycle.Add(fieldType.OriginalDefinition) + + If current.Report AndAlso Not cycleReportedForCurrentType AndAlso fieldType.OriginalDefinition.Equals(Me) Then ' the cycle includes 'current.Path' and ends with 'field'; the order is reversed in the list - Dim cycleFields = New ConsList(Of FieldSymbol)(field, current.Path) + Dim cycleFields = New ConsList(Of FieldSymbol)(field, current.FieldPath) ' generate a message info Dim diagnosticInfos = ArrayBuilder(Of DiagnosticInfo).GetInstance() @@ -2158,19 +2173,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols hasCycle = True End If - ElseIf Not data.ProcessedTypes.Contains(fieldType) Then + ElseIf Not data.ProcessedTypes.Contains(fieldType) AndAlso Not data.TypesWithCycle.Contains(fieldType.OriginalDefinition) Then ' Add to the queue if we don't know yet if it was processed - If Not fieldType.IsDefinition Then - ' Types constructed from generic types are considered to be a separate types. We never report - ' errors on such types. We also process only fields actually changed compared to original generic type. - data.Queue.Enqueue(New StructureCircularityDetectionDataSet.QueueElement( - fieldType, New ConsList(Of FieldSymbol)(field, current.Path))) - - ' The original Generic type is added using regular rules (see next note). - fieldType = fieldType.OriginalDefinition - End If - ' NOTE: we want to make sure we report the same error for the same types ' consistently and don't depend on the call order; this solution uses ' the following approach: @@ -2180,15 +2185,30 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ' (b) thus, this analysis only considers the cycles consisting of the ' types which are 'bigger' than 'structBeingAnalyzed' because we will not ' report the error regarding this cycle for this type anyway - Dim stepIntoType As Boolean = DetectTypeCircularity_ShouldStepIntoType(fieldType) - If stepIntoType Then + Dim stepIntoType As Boolean = DetectTypeCircularity_ShouldStepIntoType(fieldType.OriginalDefinition) + + If stepIntoType OrElse Not fieldType.IsDefinition Then ' enqueue to be processed + ' First, visit type as a definition in order to detect the fact that it itself has a cycle. + ' This prevents us from going into an infinite generic expansion while visiting constructed form + ' of the type below. data.Queue.Enqueue(New StructureCircularityDetectionDataSet.QueueElement( - fieldType, New ConsList(Of FieldSymbol)(field, current.Path))) + fieldType.OriginalDefinition, New ConsList(Of FieldSymbol)(field, current.FieldPath), + current.ContainingDefinitionsPath.Prepend(fieldType.OriginalDefinition), + report:=stepIntoType)) Else ' should not process data.ProcessedTypes.Add(fieldType) End If + + If Not fieldType.IsDefinition Then + ' Types constructed from generic types are considered to be a separate types. We never report + ' errors on such types. We also process only fields actually changed compared to original generic type. + data.Queue.Enqueue(New StructureCircularityDetectionDataSet.QueueElement( + fieldType, New ConsList(Of FieldSymbol)(field, current.FieldPath), + current.ContainingDefinitionsPath, + report:=True)) + End If End If End If End If diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMemberFieldSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMemberFieldSymbol.vb index 3a0251f8311d5..1e034909add1b 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMemberFieldSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMemberFieldSymbol.vb @@ -29,6 +29,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols MyBase.New(container, syntaxRef, name, memberFlags) End Sub + Protected Overrides Sub GenerateDeclarationErrorsImpl(cancellationToken As CancellationToken) + MyBase.GenerateDeclarationErrorsImpl(cancellationToken) + + If (_lazyState And StateFlags.TypeConstraintsChecked) = 0 Then + Dim sourceModule = DirectCast(Me.ContainingModule, SourceModuleSymbol) + Dim diagnostics = BindingDiagnosticBag.GetInstance() + Type.CheckAllConstraints(DeclaringCompilation.LanguageVersion, + Locations(0), diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, sourceModule.ContainingAssembly)) + sourceModule.AtomicSetFlagAndStoreDiagnostics(_lazyState, StateFlags.TypeConstraintsChecked, 0, diagnostics) + diagnostics.Free() + End If + End Sub + Friend NotOverridable Overrides ReadOnly Property DeclarationSyntax As VisualBasicSyntaxNode Get Return Syntax.Parent.Parent diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMethodSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMethodSymbol.vb index 9d17716bffd83..bcf324bcc0325 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMethodSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceMethodSymbol.vb @@ -2156,7 +2156,9 @@ lReportErrorOnTwoTokens: If param.Locations.Length > 0 Then ' Note: Errors are reported on the parameter name. Ideally, we should ' match Dev10 and report errors on the parameter type syntax instead. - param.Type.CheckAllConstraints(param.Locations(0), diagBag, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, sourceModule.ContainingAssembly)) + param.Type.CheckAllConstraints( + DeclaringCompilation.LanguageVersion, + param.Locations(0), diagBag, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, sourceModule.ContainingAssembly)) End If Next @@ -2164,7 +2166,9 @@ lReportErrorOnTwoTokens: Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - retType.CheckAllConstraints(diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, sourceModule.ContainingAssembly)) + retType.CheckAllConstraints( + DeclaringCompilation.LanguageVersion, + diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, sourceModule.ContainingAssembly)) If useSiteDiagnosticsBuilder IsNot Nothing Then diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceModuleSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceModuleSymbol.vb index 5721db14a43f3..a30a836f3121b 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceModuleSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceModuleSymbol.vb @@ -529,7 +529,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Private Sub ValidateImport(type As TypeSymbol, info As GlobalImportInfo, diagnostics As BindingDiagnosticBag) Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - type.CheckAllConstraints(diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, ContainingAssembly)) + type.CheckAllConstraints( + DeclaringCompilation.LanguageVersion, + diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, ContainingAssembly)) If useSiteDiagnosticsBuilder IsNot Nothing Then diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamedTypeSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamedTypeSymbol.vb index f540c8c35744f..abd72dfff73ac 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamedTypeSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamedTypeSymbol.vb @@ -1626,7 +1626,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim location = singleDeclaration.NameLocation diagnostics = BindingDiagnosticBag.GetInstance() - localBase.CheckAllConstraints(location, diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, m_containingModule.ContainingAssembly)) + localBase.CheckAllConstraints( + DeclaringCompilation.LanguageVersion, + location, diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, m_containingModule.ContainingAssembly)) If IsGenericType Then ' Check that generic type does not derive from System.Attribute. @@ -1678,7 +1680,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim location = singleDeclaration.NameLocation diagnostics = BindingDiagnosticBag.GetInstance() For Each [interface] In localInterfaces - [interface].CheckAllConstraints(location, diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, m_containingModule.ContainingAssembly)) + [interface].CheckAllConstraints( + DeclaringCompilation.LanguageVersion, + location, diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, m_containingModule.ContainingAssembly)) Next End If End If diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertyAccessorSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertyAccessorSymbol.vb index a34e887cd0f87..02addfd0ec070 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertyAccessorSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertyAccessorSymbol.vb @@ -191,7 +191,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance() Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing - retType.CheckAllConstraints(diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, sourceModule.ContainingAssembly)) + retType.CheckAllConstraints( + DeclaringCompilation.LanguageVersion, + diagnosticsBuilder, useSiteDiagnosticsBuilder, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, sourceModule.ContainingAssembly)) If useSiteDiagnosticsBuilder IsNot Nothing Then diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder) @@ -260,7 +262,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols If param.Locations.Length > 0 Then ' Note: Errors are reported on the parameter name. Ideally, we should ' match Dev10 and report errors on the parameter type syntax instead. - param.Type.CheckAllConstraints(param.Locations(0), diagBag, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, sourceModule.ContainingAssembly)) + param.Type.CheckAllConstraints( + DeclaringCompilation.LanguageVersion, + param.Locations(0), diagBag, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagBag, sourceModule.ContainingAssembly)) End If Next diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertySymbol.vb index 4f67c023631da..661fbf78934c2 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertySymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourcePropertySymbol.vb @@ -55,6 +55,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Private Enum StateFlags As Integer SymbolDeclaredEvent = &H1 ' Bit value for generating SymbolDeclaredEvent + + TypeConstraintsChecked = &H2 End Enum Private Sub New(container As SourceMemberContainerTypeSymbol, @@ -335,7 +337,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Property Private Function ComputeType(diagnostics As BindingDiagnosticBag) As TypeSymbol - Dim binder = CreateBinderForTypeDeclaration() + Dim binder = BinderBuilder.CreateBinderForType(DirectCast(ContainingModule, SourceModuleSymbol), _syntaxRef.SyntaxTree, _containingType) + binder = New LocationSpecificBinder(BindingLocation.PropertyType, Me, binder) If IsWithEvents Then Dim syntax = DirectCast(_syntaxRef.GetSyntax(), ModifiedIdentifierSyntax) @@ -1190,11 +1193,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols MyBase.GenerateDeclarationErrors(cancellationToken) ' Ensure return type attributes are bound - Dim unusedType = Me.Type + Dim type = Me.Type Dim unusedParameters = Me.Parameters Me.GetReturnTypeAttributesBag() Dim unusedImplementations = Me.ExplicitInterfaceImplementations + If (_lazyState And StateFlags.TypeConstraintsChecked) = 0 Then + Dim sourceModule = DirectCast(Me.ContainingModule, SourceModuleSymbol) + Dim diagnostics = BindingDiagnosticBag.GetInstance() + type.CheckAllConstraints(DeclaringCompilation.LanguageVersion, + Locations(0), diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, sourceModule.ContainingAssembly)) + sourceModule.AtomicSetFlagAndStoreDiagnostics(_lazyState, StateFlags.TypeConstraintsChecked, 0, diagnostics) + diagnostics.Free() + End If + If DeclaringCompilation.EventQueue IsNot Nothing Then DirectCast(Me.ContainingModule, SourceModuleSymbol).AtomicSetFlagAndRaiseSymbolDeclaredEvent(_lazyState, StateFlags.SymbolDeclaredEvent, 0, Me) End If diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceTypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceTypeParameterSymbol.vb index 126b2ef5c1ab5..eb20fe448f87d 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceTypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceTypeParameterSymbol.vb @@ -90,6 +90,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return False + End Get + End Property + Friend Overrides Function GetConstraints() As ImmutableArray(Of TypeParameterConstraint) EnsureAllConstraintsAreResolved() Return _lazyConstraints @@ -183,7 +189,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols constraintType.AddUseSiteInfo(useSiteInfo) If Not diagnostics.Add(location, useSiteInfo) Then - constraintType.CheckAllConstraints(location, diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, containingAssembly)) + constraintType.CheckAllConstraints( + DeclaringCompilation.LanguageVersion, + location, diagnostics, template:=New CompoundUseSiteInfo(Of AssemblySymbol)(diagnostics, containingAssembly)) End If End If Next diff --git a/src/Compilers/VisualBasic/Portable/Symbols/SubstitutedTypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/SubstitutedTypeParameterSymbol.vb index 475be49ff2a2c..461844dd4f76f 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/SubstitutedTypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/SubstitutedTypeParameterSymbol.vb @@ -98,6 +98,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return _originalDefinition.HasUnmanagedTypeConstraint + End Get + End Property + Public Overrides ReadOnly Property ContainingSymbol As Symbol Get Return _containingSymbol diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Symbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Symbol.vb index cac4cfd3bedd2..17e8d23450c6b 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Symbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Symbol.vb @@ -604,7 +604,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic SymbolKind.Event, SymbolKind.Parameter, SymbolKind.TypeParameter, - SymbolKind.ErrorType + SymbolKind.ErrorType, + SymbolKind.Preprocessing Exit Select Case SymbolKind.NamedType diff --git a/src/Compilers/VisualBasic/Portable/Symbols/SynthesizedSymbols/SynthesizedClonedTypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/SynthesizedSymbols/SynthesizedClonedTypeParameterSymbol.vb index e6a1a352fbd6d..398264f9da09f 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/SynthesizedSymbols/SynthesizedClonedTypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/SynthesizedSymbols/SynthesizedClonedTypeParameterSymbol.vb @@ -76,6 +76,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Friend Overrides ReadOnly Property HasUnmanagedTypeConstraint As Boolean + Get + Return _correspondingMethodTypeParameter.HasUnmanagedTypeConstraint + End Get + End Property + Public Overrides ReadOnly Property ContainingSymbol As Symbol Get Return _container diff --git a/src/Compilers/VisualBasic/Portable/Symbols/TypeParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/TypeParameterSymbol.vb index 184a1c7286655..603d4ca44ce2c 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/TypeParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/TypeParameterSymbol.vb @@ -319,11 +319,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public MustOverride ReadOnly Property AllowsRefLikeType As Boolean Implements ITypeParameterSymbol.AllowsRefLikeType - Private ReadOnly Property HasUnmanagedTypeConstraint As Boolean Implements ITypeParameterSymbol.HasUnmanagedTypeConstraint - Get - Return False - End Get - End Property + Friend MustOverride ReadOnly Property HasUnmanagedTypeConstraint As Boolean Implements ITypeParameterSymbol.HasUnmanagedTypeConstraint Private ReadOnly Property HasNotNullConstraint As Boolean Implements ITypeParameterSymbol.HasNotNullConstraint Get @@ -333,6 +329,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public MustOverride ReadOnly Property Variance As VarianceKind Implements ITypeParameterSymbol.Variance + Friend Overrides Function GetManagedKind(ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ManagedKind + Return If(HasUnmanagedTypeConstraint, ManagedKind.Unmanaged, ManagedKind.Managed) + End Function + ''' ''' If this is a type parameter of a reduced extension method, gets the type parameter definition that ''' this type parameter was reduced from. Otherwise, returns Nothing. diff --git a/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb index 72f9437aa8837..c6f532857bfda 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb @@ -261,6 +261,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + ' + ' Indicates whether a type is managed or not in C# terms (i.e. you can take a pointer to it). + ' + Friend MustOverride Function GetManagedKind(ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As ManagedKind + ' Only the compiler can create TypeSymbols. Friend Sub New() End Sub @@ -588,8 +593,7 @@ Done: Private ReadOnly Property ITypeSymbol_IsUnmanagedType As Boolean Implements ITypeSymbol.IsUnmanagedType Get - ' VB has no concept of unmanaged types - Return False + Return GetManagedKind(CompoundUseSiteInfo(Of AssemblySymbol).Discarded) <> ManagedKind.Managed End Get End Property diff --git a/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbolExtensions.vb b/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbolExtensions.vb index 8dbdcb3e68379..80ccdad058a12 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbolExtensions.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbolExtensions.vb @@ -1308,6 +1308,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols namedTypeSymbol.IsContainedInNamespace(NameOf(System), NameOf(System.Threading)) End Function + ' Keep in sync with C# equivalent. + + Friend Function IsWellKnownTypeUnmanagedType(typeSymbol As TypeSymbol) As Boolean + Dim namedTypeSymbol = TryCast(typeSymbol, NamedTypeSymbol) + Return namedTypeSymbol IsNot Nothing AndAlso + namedTypeSymbol.Name = "UnmanagedType" AndAlso + namedTypeSymbol.Arity = 0 AndAlso + namedTypeSymbol.ContainingType Is Nothing AndAlso + IsContainedInNamespace(typeSymbol, "System", "Runtime", "InteropServices") + End Function + Private Function IsWellKnownCompilerServicesTopLevelType(typeSymbol As TypeSymbol, name As String) As Boolean If Not String.Equals(typeSymbol.Name, name) Then diff --git a/src/Compilers/VisualBasic/Portable/VBResources.resx b/src/Compilers/VisualBasic/Portable/VBResources.resx index 6e3a339790dbb..aa32f54bfc7c7 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.resx +++ b/src/Compilers/VisualBasic/Portable/VBResources.resx @@ -2784,6 +2784,9 @@ Type argument '{0}' does not satisfy the 'Structure' constraint for type parameter '{1}'. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' does not satisfy the 'Class' constraint for type parameter '{1}'. @@ -5626,6 +5629,9 @@ assigning to or passing 'ByRef' properties with init-only setters + + recognizing 'unmanaged' constraint + Init-only property '{0}' can only be assigned by an object member initializer, or on 'Me', 'MyClass` or 'MyBase' in an instance constructor. diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf index 58a67c5e5dee6..678da8c69cc85 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf @@ -132,6 +132,11 @@ Atribut UnmanagedCallersOnly se nepodporuje. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. {0} vyžaduje funkci kompilátoru {1}, což tato verze kompilátoru Visual Basic nepodporuje. @@ -162,6 +167,11 @@ parametry neomezeného typu v binárních podmíněných výrazech + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf index 9e9ed4cc30aa5..8ec7aec8c00c2 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf @@ -132,6 +132,11 @@ Das Attribut "UnmanagedCallersOnly" wird nicht unterstützt. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. "{0}" erfordert die Compilerfunktion "{1}", die von dieser Version des Visual Basic Compilers nicht unterstützt wird. @@ -162,6 +167,11 @@ Nicht eingeschränkte Typparameter in binären bedingten Ausdrücken + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf index edd7d212905a9..bf862f7cc8d8a 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf @@ -132,6 +132,11 @@ No se admite el atributo "UnmanagedCallersOnly". + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}' requiere la característica del compilador '{1}', que no es compatible con esta versión del compilador de Visual Basic. @@ -162,6 +167,11 @@ parámetros de tipo sin restricciones en expresiones condicionales binarias + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf index e9940a1be752b..34598d12f1e3c 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf @@ -132,6 +132,11 @@ L'attribut 'UnmanagedCallersOnly' n'est pas pris en charge. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}' nécessite la fonctionnalité de compilateur '{1}', qui n’est pas prise en charge par cette version du compilateur Visual Basic. @@ -162,6 +167,11 @@ paramètres de type sans contrainte dans les expressions conditionnelles binaires + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf index 1115589772d4f..f29bdafb4d467 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf @@ -132,6 +132,11 @@ L'attributo 'UnmanagedCallersOnly' non è supportato. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}' richiede la funzionalità del compilatore '{1}', che non è supportata da questa versione del compilatore Visual Basic. @@ -162,6 +167,11 @@ parametri di tipo non senza vincoli in espressioni condizionali binarie + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf index dd1aeb31ff170..cee9b4d04c988 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf @@ -132,6 +132,11 @@ 'UnmanagedCallersOnly' 属性はサポートされていません。 + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}' にはコンパイラ機能 '{1}' が必要ですが、このバージョンのVisual Basic コンパイラではサポートされていません。 @@ -162,6 +167,11 @@ バイナリ条件式での非制約型パラメーター + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf index 7810d8be04987..a7324e86a2bb4 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf @@ -132,6 +132,11 @@ 'UnmanagedCallersOnly' 특성은 지원되지 않습니다. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}'에는 이 버전의 Visual Basic 컴파일러에서 지원하지 않는 컴파일러 기능 '{1}'이(가) 필요합니다. @@ -162,6 +167,11 @@ 이진 조건식의 비제한 형식 매개 변수 + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf index 07f875f3c3812..350bd6716bc38 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf @@ -132,6 +132,11 @@ Atrybut „UnmanagedCallersOnly” jest nieobsługiwany. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. „{0}” wymaga funkcji kompilatora „{1}”, która nie jest obsługiwana przez tę wersję kompilatora języka Visual Basic. @@ -162,6 +167,11 @@ parametry typu bez ograniczeń w binarnych wyrażeniach warunkowych + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf index 80f5bd687e3e0..2da6bfec81618 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf @@ -132,6 +132,11 @@ Não há suporte para o atributo 'UnmanagedCallersOnly'. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}' requer o recurso de compilador '{1}', o que não é suportado por esta versão do compilador do Visual Basic. @@ -162,6 +167,11 @@ parâmetros de tipo irrestritos em expressões condicionais binárias + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf index 09bf20a178096..2c2b9facbe584 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf @@ -132,6 +132,11 @@ Атрибут "UnmanagedCallersOnly" не поддерживается. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. Для "{0}" требуется функция компилятора "{1}", которая не поддерживается в этой версии компилятора Visual Basic. @@ -162,6 +167,11 @@ параметры неограниченного типа в двоичных условных выражениях + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf index 68462011b0d12..f8ee6dd28cb27 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf @@ -132,6 +132,11 @@ 'UnmanagedCallersOnly' özniteliği desteklenmez. + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}', Visual Basic derleyicisinin bu sürümü tarafından desteklenmeyen '{1}' derleyici özelliğini gerektirir. @@ -162,6 +167,11 @@ ikili koşullu ifadelerde kısıtlanmamış tür parametreleri + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf index 76bc9c7093c0c..78a655222e9b3 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf @@ -132,6 +132,11 @@ 不支持 "UnmanagedCallersOnly" 属性。 + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}' 需要编译器功能 '{1}',此版本的 Visual Basic 编译器不支持此功能。 @@ -162,6 +167,11 @@ 二进制条件表达式中的无约束类型参数 + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf index de5cea83ecad1..f081ca6a94841 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf @@ -132,6 +132,11 @@ 不支援 'UnmanagedCallersOnly' 屬性。 + + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + Type argument '{0}' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter '{1}'. + + '{0}' requires compiler feature '{1}', which is not supported by this version of the Visual Basic compiler. '{0}' 需要編譯器功能 '{1}',此版本的 Visual Basic 編譯器不支援此功能。 @@ -162,6 +167,11 @@ 二進位條件運算式中的非限制式型別參數 + + recognizing 'unmanaged' constraint + recognizing 'unmanaged' constraint + + Visual Basic Compiler Options diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index 11eebfbc837bd..bb42d5e53c829 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -39,7 +39,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CommandLine.UnitTests Private Shared ReadOnly s_basicCompilerExecutable As String = Path.Combine( Path.GetDirectoryName(GetType(CommandLineTests).Assembly.Location), Path.Combine("dependency", "vbc.exe")) - Private Shared ReadOnly s_DotnetCscRun As String = If(ExecutionConditionUtil.IsMono, "mono", String.Empty) + Private Shared ReadOnly s_DotnetCscRun As String = If(ExecutionConditionUtil.IsMonoDesktop, "mono", String.Empty) Private ReadOnly _baseDirectory As String = TempRoot.Root Private Shared ReadOnly s_defaultSdkDirectory As String = RuntimeEnvironment.GetRuntimeDirectory() @@ -1530,6 +1530,10 @@ End Module").Path parsedArgs.Errors.Verify() Assert.Equal(LanguageVersion.VisualBasic16_9, parsedArgs.ParseOptions.LanguageVersion) + parsedArgs = DefaultParse({"/langVERSION:17.13", "a.vb"}, _baseDirectory) + parsedArgs.Errors.Verify() + Assert.Equal(LanguageVersion.VisualBasic17_13, parsedArgs.ParseOptions.LanguageVersion) + ' The canary check is a reminder that this test needs to be updated when a language version is added LanguageVersionAdded_Canary() @@ -2055,7 +2059,7 @@ End Module").Path ' - update the "UpgradeProject" codefixer (not yet supported in VB) ' - update all the tests that call this canary ' - update the command-line documentation (CommandLine.md) - AssertEx.SetEqual({"default", "9", "10", "11", "12", "14", "15", "15.3", "15.5", "16", "16.9", "latest"}, + AssertEx.SetEqual({"default", "9", "10", "11", "12", "14", "15", "15.3", "15.5", "16", "16.9", "17.13", "latest"}, System.Enum.GetValues(GetType(LanguageVersion)).Cast(Of LanguageVersion)().Select(Function(v) v.ToDisplayString())) ' For minor versions, the format should be "x.y", such as "15.3" End Sub @@ -2077,7 +2081,8 @@ End Module").Path "15.3", "15.5", "16", - "16.9" + "16.9", + "17.13" } AssertEx.SetEqual(versions, errorCodes) @@ -2098,6 +2103,7 @@ End Module").Path Assert.Equal(LanguageVersion.VisualBasic15_5, LanguageVersion.VisualBasic15_5.MapSpecifiedToEffectiveVersion()) Assert.Equal(LanguageVersion.VisualBasic16, LanguageVersion.VisualBasic16.MapSpecifiedToEffectiveVersion()) Assert.Equal(LanguageVersion.VisualBasic16_9, LanguageVersion.VisualBasic16_9.MapSpecifiedToEffectiveVersion()) + Assert.Equal(LanguageVersion.VisualBasic17_13, LanguageVersion.VisualBasic17_13.MapSpecifiedToEffectiveVersion()) ' The canary check is a reminder that this test needs to be updated when a language version is added LanguageVersionAdded_Canary() @@ -2121,6 +2127,7 @@ End Module").Path InlineData("16", True, LanguageVersion.VisualBasic16), InlineData("16.0", True, LanguageVersion.VisualBasic16), InlineData("16.9", True, LanguageVersion.VisualBasic16_9), + InlineData("17.13", True, LanguageVersion.VisualBasic17_13), InlineData("DEFAULT", True, LanguageVersion.Default), InlineData("default", True, LanguageVersion.Default), InlineData("LATEST", True, LanguageVersion.Latest), @@ -5019,7 +5026,7 @@ End Class End Sub - Public Sub UnableWriteOutput() + Public Sub UnableWriteOutput_OutputFileIsDirectory() Dim tempFolder = Temp.CreateDirectory() Dim baseDirectory = tempFolder.ToString() Dim subFolder = tempFolder.CreateDirectory("temp.dll") @@ -5035,6 +5042,34 @@ End Class CleanupAllGeneratedFiles(src.Path) End Sub + + Public Sub UnableWriteOutput_OutputFileLocked() + Dim tempFolder = Temp.CreateDirectory() + Dim baseDirectory = tempFolder.ToString() + Dim filePath = tempFolder.CreateFile("temp.dll").Path + + Dim src = Temp.CreateFile("a.vb") + src.WriteAllText("Imports System") + + Using New FileStream(filePath, FileMode.Open, FileAccess.Write, FileShare.None) + Dim currentProcess = Process.GetCurrentProcess() + + Dim outWriter As New StringWriter() + Dim exitCode As Integer = New MockVisualBasicCompiler(Nothing, baseDirectory, {"/nologo", "/preferreduilang:en", "/t:library", "/out:" & filePath, src.ToString()}).Run(outWriter, Nothing) + Assert.Equal(1, exitCode) + Dim output = outWriter.ToString().Trim() + + Dim pattern = "vbc : error BC2012: can't open '(?.*)' for writing: (?.*); file may be locked by '(?.*)' \((?.*)\)" + Dim match = Regex.Match(output, pattern) + Assert.True(match.Success, $"Expected pattern:{Environment.NewLine}{pattern}{Environment.NewLine}Actual:{Environment.NewLine}{output}") + Assert.Equal(filePath, match.Groups("path").Value) + Assert.Contains("testhost", match.Groups("app").Value) + Assert.Equal(currentProcess.Id, Integer.Parse(match.Groups("pid").Value)) + End Using + + CleanupAllGeneratedFiles(src.Path) + End Sub + Public Sub SdkPathAndLibEnvVariable() Dim parsedArgs = DefaultParse({"/libpath:c:lib2", "/sdkpath:<>,d:\sdk1", "/vbruntime*", "/nostdlib", "a.vb"}, _baseDirectory) diff --git a/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb b/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb index d67a719f84de7..3a01a511788d1 100644 --- a/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb +++ b/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb @@ -14448,7 +14448,7 @@ End Class BC32106: Type argument 'T' does not satisfy the 'Class' constraint for type parameter 'T2'. Dim field As List(Of (T, T)) - ~ + ~~~~~ BC32106: Type argument 'U' does not satisfy the 'Class' constraint for type parameter 'T2'. Function M(Of U)(x As U) As (U, U) ~~~~~~ @@ -14516,7 +14516,7 @@ End Class BC32105: Type argument 'T' does not satisfy the 'Structure' constraint for type parameter 'T2'. Dim field As List(Of (T, T)) - ~ + ~~~~~ BC32105: Type argument 'U' does not satisfy the 'Structure' constraint for type parameter 'T2'. Function M(Of U As Class)(x As (U, U)) As (U, U) ~ diff --git a/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb b/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb index fa7e860d7c8c4..7a317369b781d 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Compilation/CompilationAPITests.vb @@ -2324,10 +2324,10 @@ End Class Public Sub ReferenceManagerReuse_WithSyntaxTrees() Dim ta = Parse("Imports System") - Dim tb = Parse("Imports System", options:=TestOptions.Script) + Dim tb = Parse("Imports System", options:=TestOptions.Script.WithLanguageVersion(LanguageVersion.Latest)) Dim tc = Parse("#r ""bar"" ' error: #r in regular code") - Dim tr = Parse("#r ""goo""", options:=TestOptions.Script) - Dim ts = Parse("#r ""bar""", options:=TestOptions.Script) + Dim tr = Parse("#r ""goo""", options:=TestOptions.Script.WithLanguageVersion(LanguageVersion.Latest)) + Dim ts = Parse("#r ""bar""", options:=TestOptions.Script.WithLanguageVersion(LanguageVersion.Latest)) Dim a = VisualBasicCompilation.Create("c", syntaxTrees:={ta}) diff --git a/src/Compilers/VisualBasic/Test/Semantic/Compilation/SemanticModelAPITests.vb b/src/Compilers/VisualBasic/Test/Semantic/Compilation/SemanticModelAPITests.vb index b91a3444a2145..6d68af2976416 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Compilation/SemanticModelAPITests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Compilation/SemanticModelAPITests.vb @@ -2947,12 +2947,12 @@ End Class") Single(Function(n) n.Identifier.ValueText = name) Return CType(model.GetDeclaredSymbol(decl), ILocalSymbol).Type End Function - ' VB does not have a concept of a managed type - Assert.False(getLocalType("s1").IsUnmanagedType) - Assert.False(getLocalType("s2").IsUnmanagedType) + + Assert.True(getLocalType("s1").IsUnmanagedType) + Assert.True(getLocalType("s2").IsUnmanagedType) Assert.False(getLocalType("s3").IsUnmanagedType) - Assert.False(getLocalType("s4").IsUnmanagedType) - Assert.False(getLocalType("e1").IsUnmanagedType) + Assert.True(getLocalType("s4").IsUnmanagedType) + Assert.True(getLocalType("e1").IsUnmanagedType) End Sub @@ -4897,5 +4897,24 @@ BC30990: Member 'Key' cannot be initialized in an object initializer expression ) End Sub + + Public Sub CommonPreprocessingSymbolProperties() + Dim compilation = CompilationUtils.CreateCompilationWithMscorlib40( + + +#If NET5_0_OR_GREATER +#End If + +) + + Dim tree = CompilationUtils.GetTree(compilation, "a.vb") + Dim semanticModel = compilation.GetSemanticModel(tree) + Dim node = tree.GetCompilationUnitRoot().DescendantNodes(descendIntoTrivia:=True).OfType(Of IdentifierNameSyntax).First() + Dim symbol = semanticModel.GetPreprocessingSymbolInfo(node).Symbol + Assert.NotNull(symbol) + Assert.Equal("NET5_0_OR_GREATER", symbol.Name) + Assert.True(symbol.CanBeReferencedByName) + End Sub + End Class End Namespace diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb index fcf63a3f1fe0b..89998c076ec36 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb @@ -5154,5 +5154,39 @@ True CompileAndVerify(compilation, expectedOutput:=expectedOutput).VerifyDiagnostics() End Sub + + Public Sub GetSymbolInfo_ExplicitCastOnMethodGroup() + Dim compilation = CreateCompilation( + + + ) + + compilation.AssertTheseEmitDiagnostics( +BC30581: 'AddressOf' expression cannot be converted to 'C' because 'C' is not a delegate type. + Dim x As C = DirectCast(AddressOf C.Test, C) + ~~~~~~~~~~~~~~~~ +) + + Dim tree = compilation.SyntaxTrees.Single() + Dim model = compilation.GetSemanticModel(tree) + Dim syntax = tree.GetRoot().DescendantNodes().OfType(Of UnaryExpressionSyntax)().Single() + Assert.Null(model.GetSymbolInfo(syntax).Symbol) + Assert.Null(model.GetSymbolInfo(syntax.Operand).Symbol) + End Sub + End Class End Namespace diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/OverloadResolution.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/OverloadResolution.vb index b3944b3117778..28420b328047d 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Semantics/OverloadResolution.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/OverloadResolution.vb @@ -3050,7 +3050,7 @@ Class OptionStrictOff End Class - Dim optionStrictOffTree = VisualBasicSyntaxTree.ParseText(optionStrictOff.Value) + Dim optionStrictOffTree = VisualBasicSyntaxTree.ParseText(optionStrictOff.Value, options:=TestOptions.RegularLatest) Dim c1 = VisualBasicCompilation.Create("Test1", syntaxTrees:={Parse(SemanticResourceUtil.OverloadResolutionTestSource), optionStrictOffTree}, diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb index e67cb60331d87..5f52b065c237d 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb @@ -1552,7 +1552,7 @@ end class Dim format = New SymbolDisplayFormat( memberOptions:=SymbolDisplayMemberOptions.IncludeType, kindOptions:=SymbolDisplayKindOptions.IncludeMemberKeyword, - compilerInternalOptions:=SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) + compilerInternalOptions:=SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) TestSymbolDescription( text, @@ -6001,6 +6001,37 @@ end class" SymbolDisplayPartKind.Punctuation) End Sub + + Public Sub PreprocessingSymbol() + Dim source = +" +#If NET5_0_OR_GREATER +#End If" + Dim format = New SymbolDisplayFormat( + memberOptions:=SymbolDisplayMemberOptions.IncludeParameters Or SymbolDisplayMemberOptions.IncludeType Or SymbolDisplayMemberOptions.IncludeModifiers, + miscellaneousOptions:=SymbolDisplayMiscellaneousOptions.UseSpecialTypes) + + Dim comp = CreateCompilation(source) + Dim tree = comp.SyntaxTrees.First() + Dim model = comp.GetSemanticModel(tree) + Dim preprocessingNameSyntax = tree.GetRoot().DescendantNodes(descendIntoTrivia:=True).OfType(Of IdentifierNameSyntax).First() + Dim preprocessingSymbolInfo = model.GetPreprocessingSymbolInfo(preprocessingNameSyntax) + Dim preprocessingSymbol = preprocessingSymbolInfo.Symbol + + Assert.Equal( + "NET5_0_OR_GREATER", + SymbolDisplay.ToDisplayString(preprocessingSymbol, format)) + + Dim displayParts = preprocessingSymbol.ToDisplayParts(format) + Dim expectedDisplayParts = + { + New SymbolDisplayPart(SymbolDisplayPartKind.Text, preprocessingSymbol, "NET5_0_OR_GREATER") + } + Assert.Equal( + expected:=expectedDisplayParts, + actual:=displayParts) + End Sub + #Region "Helpers" Private Shared Sub TestSymbolDescription( diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/GenericConstraintTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/GenericConstraintTests.vb index 0f6ea6c15d133..55c1d3224dc62 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/GenericConstraintTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/GenericConstraintTests.vb @@ -4686,7 +4686,7 @@ End Interface compilation.AssertTheseDiagnostics( BC32044: Type argument 'String' does not inherit from or implement the constraint type 'IStoreable'. Public ReadOnly Property Deleted As IEnumerable(Of UpdateResult(Of String)) - ~~~~~~ + ~~~~~~~ ) End Sub @@ -5014,10 +5014,10 @@ Delegate Sub D(Of T As New)() compilation.AssertTheseDiagnostics( BC32044: Type argument 'C3T2' does not inherit from or implement the constraint type 'Integer'. Dim x As C1(Of Integer, Integer).C2(Of C2T1, C2T2).C3(Of C3T1, C3T2) - ~~~~ + ~ ) Assert.Throws(Of InvalidOperationException)(Sub() c5.Construct(c1)) diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/LoadCustomModifiers.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/LoadCustomModifiers.vb index b08336c56d925..58edd937fbfc4 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/LoadCustomModifiers.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/LoadCustomModifiers.vb @@ -110,7 +110,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Symbols.Metadata.PE End Sub - Public Sub UnmanagedConstraint_RejectedSymbol_OnClass() + Public Sub UnmanagedConstraint_OnClass() Dim reference = CreateCSharpCompilation(" public class TestRef where T : unmanaged { @@ -122,6 +122,7 @@ public class TestRef where T : unmanaged Class Test Shared Sub Main() Dim x = New TestRef(Of String)() + Dim y = New TestRef(Of Integer)() End Sub End Class @@ -129,24 +130,63 @@ End Class Dim compilation = CreateCompilationWithMscorlib45AndVBRuntime(source, references:={reference}) - AssertTheseDiagnostics(compilation, -BC30649: '' is an unsupported type. + Dim errs As XElement = + +BC32105: Type argument 'String' does not satisfy the 'Structure' constraint for type parameter 'T'. Dim x = New TestRef(Of String)() ~~~~~~ -BC32044: Type argument 'String' does not inherit from or implement the constraint type '?'. +BC37332: Type argument 'String' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. Dim x = New TestRef(Of String)() ~~~~~~ + + + AssertTheseDiagnostics(compilation, errs) + + Dim typeParameter = compilation.GetTypeByMetadataName("TestRef`1").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.True(typeParameter.HasUnmanagedTypeConstraint) + + typeParameter = compilation.GetTypeByMetadataName("System.Nullable`1").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.False(typeParameter.HasUnmanagedTypeConstraint) + + compilation = CreateCompilationWithMscorlib45AndVBRuntime(source, references:={reference}, parseOptions:=TestOptions.Regular17_13) + AssertTheseDiagnostics(compilation, errs) + + typeParameter = compilation.GetTypeByMetadataName("TestRef`1").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.True(typeParameter.HasUnmanagedTypeConstraint) + compilation = CreateCompilationWithMscorlib45AndVBRuntime(source, references:={reference}, parseOptions:=TestOptions.RegularLatest) + + AssertTheseDiagnostics(compilation, errs) + + typeParameter = compilation.GetTypeByMetadataName("TestRef`1").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.True(typeParameter.HasUnmanagedTypeConstraint) + + compilation = CreateCompilationWithMscorlib45AndVBRuntime(source, references:={reference}, parseOptions:=TestOptions.Regular16_9) + AssertTheseDiagnostics(compilation, BC32105: Type argument 'String' does not satisfy the 'Structure' constraint for type parameter 'T'. Dim x = New TestRef(Of String)() ~~~~~~ +BC36716: Visual Basic 16.9 does not support recognizing 'unmanaged' constraint. + Dim x = New TestRef(Of String)() + ~~~~~~ +BC37332: Type argument 'String' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. + Dim x = New TestRef(Of String)() + ~~~~~~ +BC36716: Visual Basic 16.9 does not support recognizing 'unmanaged' constraint. + Dim y = New TestRef(Of Integer)() + ~~~~~~~ ) - Dim badTypeParameter = compilation.GetTypeByMetadataName("TestRef`1").TypeParameters.Single() - Assert.True(badTypeParameter.HasValueTypeConstraint) + typeParameter = compilation.GetTypeByMetadataName("TestRef`1").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.True(typeParameter.HasUnmanagedTypeConstraint) End Sub - Public Sub UnmanagedConstraint_RejectedSymbol_OnMethod() + Public Sub UnmanagedConstraint_OnMethod() Dim reference = CreateCSharpCompilation(" public class TestRef { @@ -170,17 +210,21 @@ End Class Dim compilation = CreateCompilationWithMscorlib45AndVBRuntime(source, references:={reference}) AssertTheseDiagnostics(compilation, -BC30649: '' is an unsupported type. +BC32105: Type argument 'String' does not satisfy the 'Structure' constraint for type parameter 'T'. + x.M(Of String)() + ~~~~~~~~~~~~ +BC37332: Type argument 'String' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. x.M(Of String)() - ~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~ ) - Dim badTypeParameter = compilation.GetTypeByMetadataName("TestRef").GetMethod("M").TypeParameters.Single() - Assert.True(badTypeParameter.HasValueTypeConstraint) + Dim typeParameter = compilation.GetTypeByMetadataName("TestRef").GetMethod("M").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.True(typeParameter.HasUnmanagedTypeConstraint) End Sub - Public Sub UnmanagedConstraint_RejectedSymbol_OnDelegate() + Public Sub UnmanagedConstraint_OnDelegate() Dim reference = CreateCSharpCompilation(" public delegate T D() where T : unmanaged; ", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() @@ -192,25 +236,23 @@ Class Test Shared Sub Main(del As D(Of String)) End Sub End Class - + Dim compilation = CreateCompilationWithMscorlib45AndVBRuntime(source, references:={reference}) AssertTheseDiagnostics(compilation, -BC30649: '' is an unsupported type. - Shared Sub Main(del As D(Of String)) - ~~~ -BC32044: Type argument 'String' does not inherit from or implement the constraint type '?'. +BC32105: Type argument 'String' does not satisfy the 'Structure' constraint for type parameter 'T'. Shared Sub Main(del As D(Of String)) ~~~ -BC32105: Type argument 'String' does not satisfy the 'Structure' constraint for type parameter 'T'. +BC37332: Type argument 'String' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. Shared Sub Main(del As D(Of String)) ~~~ ) - Dim badTypeParameter = compilation.GetTypeByMetadataName("D`1").TypeParameters.Single() - Assert.True(badTypeParameter.HasValueTypeConstraint) + Dim typeParameter = compilation.GetTypeByMetadataName("D`1").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.True(typeParameter.HasUnmanagedTypeConstraint) End Sub End Class diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/TypeTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/TypeTests.vb index 047f6779857aa..02c20c77f0dfc 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/TypeTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/TypeTests.vb @@ -1538,6 +1538,239 @@ BC30294: Structure 'SI_1' cannot contain an instance of itself: ) End Sub + + + Public Sub InstanceMemberExplosion_01() + Dim compilation = CompilationUtils.CreateCompilation(" +Structure A(Of T) + Dim x As A(Of A(Of T)) +End Structure + +Structure B(Of T) + Dim x As A(Of B(Of T)) +End Structure + +Structure C(Of T) + Dim x As D(Of T) +End Structure + +Structure D(Of T) + Dim x As C(Of D(Of T)) +End Structure +") + + CompilationUtils.AssertTheseDiagnostics(compilation, + +BC30294: Structure 'A' cannot contain an instance of itself: + 'A(Of T)' contains 'A(Of A(Of T))' (variable 'x'). + Dim x As A(Of A(Of T)) + ~ +BC30294: Structure 'C' cannot contain an instance of itself: + 'C(Of T)' contains 'D(Of T)' (variable 'x'). + 'D(Of T)' contains 'C(Of D(Of T))' (variable 'x'). + Dim x As D(Of T) + ~ +BC30294: Structure 'C' cannot contain an instance of itself: + 'C(Of T)' contains 'D(Of T)' (variable 'x'). + 'D(Of T)' contains 'C(Of D(Of T))' (variable 'x'). + Dim x As D(Of T) + ~ +BC30294: Structure 'D' cannot contain an instance of itself: + 'D(Of T)' contains 'C(Of D(Of T))' (variable 'x'). + 'C(Of D(Of T))' contains 'D(Of D(Of T))' (variable 'x'). + Dim x As C(Of D(Of T)) + ~ +) + End Sub + + + + Public Sub InstanceMemberExplosion_02() + Dim compilation = CompilationUtils.CreateCompilation(" +Structure A(Of T) + Dim x As A(Of A(Of T)) +End Structure + +Structure B(Of T) + Dim x As A(Of C(Of B(Of T))) +End Structure + +Structure C(Of T) +End Structure +") + + CompilationUtils.AssertTheseDiagnostics(compilation, + +BC30294: Structure 'A' cannot contain an instance of itself: + 'A(Of T)' contains 'A(Of A(Of T))' (variable 'x'). + Dim x As A(Of A(Of T)) + ~ +) + End Sub + + + + Public Sub InstanceMemberExplosion_04() + Dim compilation = CompilationUtils.CreateCompilation(" +Structure A(Of T) + Dim x As A(Of A(Of T)) +End Structure + +Structure C(Of T) + Dim x As C(Of C(Of T)) +End Structure + +Structure B(Of T) + Dim x As A(Of B(Of T)) + Dim y As C(Of C(Of T)) + Dim z As B(Of T) +End Structure + +Structure D + Dim x As B(Of Integer) +End Structure +") + + CompilationUtils.AssertTheseDiagnostics(compilation, + +BC30294: Structure 'A' cannot contain an instance of itself: + 'A(Of T)' contains 'A(Of A(Of T))' (variable 'x'). + Dim x As A(Of A(Of T)) + ~ +BC30294: Structure 'C' cannot contain an instance of itself: + 'C(Of T)' contains 'C(Of C(Of T))' (variable 'x'). + Dim x As C(Of C(Of T)) + ~ +BC30294: Structure 'B' cannot contain an instance of itself: + 'B(Of T)' contains 'B(Of T)' (variable 'z'). + Dim z As B(Of T) + ~ +) + End Sub + + + + Public Sub InstanceMemberExplosion_05() + Dim compilation = CompilationUtils.CreateCompilation(" +Structure A(Of T) + Dim x As A(Of A(Of T)) +End Structure + +Structure C(Of T) + Dim x As C(Of C(Of T)) +End Structure + +Structure B(Of T) + Dim z As B(Of T) + Dim x As A(Of B(Of T)) + Dim y As C(Of C(Of T)) +End Structure + +Structure D + Dim x As B(Of Integer) +End Structure +") + + CompilationUtils.AssertTheseDiagnostics(compilation, + +BC30294: Structure 'A' cannot contain an instance of itself: + 'A(Of T)' contains 'A(Of A(Of T))' (variable 'x'). + Dim x As A(Of A(Of T)) + ~ +BC30294: Structure 'C' cannot contain an instance of itself: + 'C(Of T)' contains 'C(Of C(Of T))' (variable 'x'). + Dim x As C(Of C(Of T)) + ~ +BC30294: Structure 'B' cannot contain an instance of itself: + 'B(Of T)' contains 'B(Of T)' (variable 'z'). + Dim z As B(Of T) + ~ +) + End Sub + + + + Public Sub InstanceMemberExplosion_06() + Dim compilation = CompilationUtils.CreateCompilation(" +Structure A(Of T) + Dim x As A(Of A(Of T)) +End Structure + +Structure C(Of T) + Dim x As C(Of C(Of T)) +End Structure + +Structure B(Of T) + Dim x As A(Of B(Of T)) + Dim z As B(Of T) + Dim y As C(Of C(Of T)) +End Structure + +Structure D + Dim x As B(Of Integer) +End Structure +") + + CompilationUtils.AssertTheseDiagnostics(compilation, + +BC30294: Structure 'A' cannot contain an instance of itself: + 'A(Of T)' contains 'A(Of A(Of T))' (variable 'x'). + Dim x As A(Of A(Of T)) + ~ +BC30294: Structure 'C' cannot contain an instance of itself: + 'C(Of T)' contains 'C(Of C(Of T))' (variable 'x'). + Dim x As C(Of C(Of T)) + ~ +BC30294: Structure 'B' cannot contain an instance of itself: + 'B(Of T)' contains 'B(Of T)' (variable 'z'). + Dim z As B(Of T) + ~ +) + End Sub + + + + Public Sub StaticMemberExplosion_01() + Dim compilation = CompilationUtils.CreateCompilation(" +Structure A(Of T) + Shared x As A(Of A(Of T)) +End Structure + +Structure B(Of T) + Shared x As A(Of B(Of T)) +End Structure + +Structure C(Of T) + Shared x As D(Of T) +End Structure + +Structure D(Of T) + Shared x As C(Of D(Of T)) +End Structure +") + + CompileAndVerify(compilation, verify:=Verification.Skipped).VerifyDiagnostics() + End Sub + + + + Public Sub StaticMemberExplosion_02() + Dim compilation = CompilationUtils.CreateCompilation(" +Structure A(Of T) + Shared x As A(Of A(Of T)) +End Structure + +Structure B(Of T) + Shared x As A(Of C(Of B(Of T))) +End Structure + +Structure C(Of T) +End Structure +") + + CompileAndVerify(compilation, verify:=Verification.Skipped).VerifyDiagnostics() + End Sub + Public Sub SynthesizedConstructorLocation() Dim compilation = CompilationUtils.CreateCompilationWithMscorlib40( diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/SymbolErrorTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/SymbolErrorTests.vb index 131c09d199002..e32077a0bb2bc 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/SymbolErrorTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/SymbolErrorTests.vb @@ -14655,7 +14655,7 @@ BC32081: 'New' constraint cannot be specified multiple times for the same type p Dim expectedErrors1 = CompilationUtils.AssertTheseDiagnostics(compilation1, expectedErrors1) End Sub diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/UnmanagedTypeConstraintTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/UnmanagedTypeConstraintTests.vb new file mode 100644 index 0000000000000..8730118939182 --- /dev/null +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/UnmanagedTypeConstraintTests.vb @@ -0,0 +1,1887 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Runtime.InteropServices +Imports Microsoft.CodeAnalysis.CSharp +Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Microsoft.CodeAnalysis.VisualBasic.Symbols +Imports Roslyn.Test.Utilities + +Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests + + Public Class UnmanagedTypeConstraintTests + Inherits BasicTestBase + + + Public Sub LoadingADifferentModifierTypeForUnmanagedConstraint() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig instance void + M2() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M2 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + dim obj = new TestRef() + + obj.M1(Of Integer)() ' valid + obj.M2(Of Integer)() ' invalid + End Sub +End Class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: '' is an unsupported type. + obj.M2(Of Integer)() ' invalid + ~~~~~~~~~~~~~~~~~~~~ +BC30649: 'T' is an unsupported type. + obj.M2(Of Integer)() ' invalid + ~~~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub LoadingUnmanagedTypeModifier_OptionalIsError() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig instance void + M2() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M2 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M1(Of Integer)() ' valid + obj.M2(Of Integer)() ' invalid + end sub +end class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: '' is an unsupported type. + obj.M2(Of Integer)() ' invalid + ~~~~~~~~~~~~~~~~~~~~ +BC30649: 'T' is an unsupported type. + obj.M2(Of Integer)() ' invalid + ~~~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub LoadingUnmanagedTypeModifier_MoreThanOneModifier() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig instance void + M2() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M2 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M1(Of Integer)() ' valid + obj.M2(Of Integer)() ' invalid + end sub +end class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: '' is an unsupported type. + obj.M2(Of Integer)() ' invalid + ~~~~~~~~~~~~~~~~~~~~ +BC30649: 'T' is an unsupported type. + obj.M2(Of Integer)() ' invalid + ~~~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub LoadingUnmanagedTypeModifier_ModreqWithoutAttribute() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig instance void + M2() cil managed + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M2 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M1(Of Integer)() ' valid + obj.M2(Of Integer)() ' invalid + end sub +end class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: 'T' is an unsupported type. + obj.M2(Of Integer)() ' invalid + ~~~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub LoadingUnmanagedTypeModifier_ModreqGeneric() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor +} + +.class public auto ansi beforefieldinit System.Runtime.InteropServices.UnmanagedType`1 + extends [mscorlib]System.Object +{ + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } +} +" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M1(Of Integer)() + end sub +end class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: '' is an unsupported type. + obj.M1(Of Integer)() + ~~~~~~~~~~~~~~~~~~~~ +BC30649: 'T' is an unsupported type. + obj.M1(Of Integer)() + ~~~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub LoadingUnmanagedTypeModifier_AttributeWithoutModreq() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M1() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M1 + + .method public hidebysig instance void + M2() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M2 + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M1(Of Integer)() ' valid + obj.M2(Of Integer)() ' invalid + end sub +end class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: 'T' is an unsupported type. + obj.M2(Of Integer)() ' invalid + ~~~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub UnmanagedTypeModreqOnOverriddenMethod() + Dim reference = CreateCSharpCompilation(" +public class Parent +{ + public virtual string M() where T : unmanaged => ""Parent""; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Child + Inherits Parent + + public overrides Function M(Of T as Structure)() As string + Return ""Child"" + End Function +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + + Dim typeParameter = compilation.GetTypeByMetadataName("Parent").GetMethod("M").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.True(typeParameter.HasUnmanagedTypeConstraint) + + typeParameter = compilation.GetTypeByMetadataName("Child").GetMethod("M").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.False(typeParameter.HasUnmanagedTypeConstraint) + + AssertTheseDiagnostics(compilation, +BC32077: 'Public Overrides Function M(Of T As Structure)() As String' cannot override 'Public Overridable Overloads Function M(Of T As Structure)() As String' because they differ by type parameter constraints. + public overrides Function M(Of T as Structure)() As string + ~ + ) + End Sub + + + Public Sub UnmanagedTypeModreqOnImplementedMethod() + Dim reference = CreateCSharpCompilation(" +public interface Parent +{ + string M() where T : unmanaged; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Child + Implements Parent + + Function M(Of T as Structure)() As string Implements Parent.M + Return ""Child"" + End Function +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + + Dim typeParameter = compilation.GetTypeByMetadataName("Parent").GetMethod("M").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.True(typeParameter.HasUnmanagedTypeConstraint) + + typeParameter = compilation.GetTypeByMetadataName("Child").GetMethod("M").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.False(typeParameter.HasUnmanagedTypeConstraint) + + AssertTheseDiagnostics(compilation, +BC32078: 'Public Function M(Of T As Structure)() As String' cannot implement 'Parent.Function M(Of T As Structure)() As String' because they differ by type parameter constraints. + Function M(Of T as Structure)() As string Implements Parent.M + ~~~~~~~~ + ) + End Sub + + + Public Sub UnmanagedConstraintWithClassConstraint_IL() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M(Of integer)() + obj.M(Of string)() + end sub +end class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: 'T' is an unsupported type. + obj.M(Of integer)() + ~~~~~~~~~~~~~~~~~~~ +BC30649: 'T' is an unsupported type. + obj.M(Of string)() + ~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub UnmanagedConstraintWithConstructorConstraint_IL() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M<.ctor (class [mscorlib]System.ValueType modreq([mscorlib]System.Runtime.InteropServices.UnmanagedType)) T>() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M(Of integer)() + obj.M(Of string)() + end sub +end class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: 'T' is an unsupported type. + obj.M(Of integer)() + ~~~~~~~~~~~~~~~~~~~ +BC30649: 'T' is an unsupported type. + obj.M(Of string)() + ~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub UnmanagedConstraintWithoutValueTypeConstraint_IL() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M<(class [mscorlib]System.ValueType modreq([mscorlib]System.Runtime.InteropServices.UnmanagedType)) T>() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M(Of integer)() + obj.M(Of string)() + end sub +end class +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC30649: 'T' is an unsupported type. + obj.M(Of integer)() + ~~~~~~~~~~~~~~~~~~~ +BC30649: 'T' is an unsupported type. + obj.M(Of string)() + ~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub UnmanagedConstraintWithTypeConstraint_IL() + + Dim ilSource = IsUnmanagedAttributeIL + " +.class public auto ansi beforefieldinit TestRef + extends [mscorlib]System.Object +{ + .method public hidebysig instance void + M() cil managed + { + .param type T + .custom instance void System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method TestRef::M + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method TestRef::.ctor + +}" + + Dim reference = CompileIL(ilSource, prependDefaultHeader:=False) + + Dim code = " +public class Test + public shared sub Main() + Dim obj = new TestRef() + + obj.M(Of integer)() + obj.M(Of string)() + obj.M(Of S1)() + end sub +end class + +Structure S1 +End Structure +" + + CreateCompilation(code, references:={reference}). + AssertTheseDiagnostics( +BC32105: Type argument 'String' does not satisfy the 'Structure' constraint for type parameter 'T'. + obj.M(Of string)() + ~~~~~~~~~~~~ +BC37332: Type argument 'String' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. + obj.M(Of string)() + ~~~~~~~~~~~~ +BC32044: Type argument 'S1' does not inherit from or implement the constraint type 'IComparable'. + obj.M(Of S1)() + ~~~~~~~~ + ) + End Sub + + + Public Sub UnmanagedTypeModreqNotSet() + Dim reference = CreateCSharpCompilation(" +public interface Parent +{ + string M() where T : struct; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Child + Implements Parent + + Function M(Of T as Structure)() As string Implements Parent.M + Return ""Child"" + End Function +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + + Dim typeParameter = compilation.GetTypeByMetadataName("Parent").GetMethod("M").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.False(typeParameter.HasUnmanagedTypeConstraint) + + typeParameter = compilation.GetTypeByMetadataName("Child").GetMethod("M").TypeParameters.Single() + Assert.True(typeParameter.HasValueTypeConstraint) + Assert.False(typeParameter.HasUnmanagedTypeConstraint) + + AssertNoDiagnostics(compilation) + End Sub + + + Public Sub UnmanagedCheck_Array() + Dim reference = CreateCSharpCompilation(" +public class Test where T : unmanaged +{ +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Dim o As Object + o = GetType(Test(Of Integer())) + o = GetType(Test(Of Integer()())) + o = GetType(Test(Of Integer(,))) + o = GetType(Test(Of Integer)) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, +BC32105: Type argument 'Integer()' does not satisfy the 'Structure' constraint for type parameter 'T'. + o = GetType(Test(Of Integer())) + ~~~~~~~~~ +BC37332: Type argument 'Integer()' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. + o = GetType(Test(Of Integer())) + ~~~~~~~~~ +BC32105: Type argument 'Integer()()' does not satisfy the 'Structure' constraint for type parameter 'T'. + o = GetType(Test(Of Integer()())) + ~~~~~~~~~~~ +BC37332: Type argument 'Integer()()' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. + o = GetType(Test(Of Integer()())) + ~~~~~~~~~~~ +BC32105: Type argument 'Integer(*,*)' does not satisfy the 'Structure' constraint for type parameter 'T'. + o = GetType(Test(Of Integer(,))) + ~~~~~~~~~~ +BC37332: Type argument 'Integer(*,*)' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. + o = GetType(Test(Of Integer(,))) + ~~~~~~~~~~ + ) + + compilation = CreateCompilation(source, references:={reference}, parseOptions:=TestOptions.Regular17_13) + AssertTheseDiagnostics(compilation, ) + + compilation = CreateCompilation(source, references:={reference}, parseOptions:=TestOptions.Regular16_9) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_AnonymousType() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Dim o As New Test + o.M(new with {.A = 1}) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ' does not satisfy the 'Structure' constraint for type parameter 'T'. + o.M(new with {.A = 1}) + ~ +BC37332: Type argument '' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as type parameter 'T'. + o.M(new with {.A = 1}) + ~ + ]]>) + End Sub + + + Public Sub UnmanagedCheck_TypedReference() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Dim o As New Test + o.M(CType(Nothing, System.TypedReference)) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_RefStruct() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged, allows ref struct {} + + void Tst() + { + this.M((RefS)default); + } +} + +public ref struct RefS { } +public ref struct RefG { public T field; } +public ref struct Ref { ref int field; } +public ref struct StructWithIndirectRefField +{ + public Ref Field; +} +public ref struct StructWithIndirectRefField2 +{ + public StructWithRefField Field; +} +public ref struct StructWithRefField +{ + public ref T RefField; +} + +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest), referencedAssemblies:=Basic.Reference.Assemblies.Net90.References.All).EmitToImageReference() + + Dim source = " +public class Program + + Shared Sub Main + Dim o As New Test + o.M(CType(Nothing, RefS)) + o.M(CType(Nothing, RefG(Of String))) + o.M(CType(Nothing, Ref)) + o.M(CType(Nothing, StructWithIndirectRefField)) + o.M(CType(Nothing, StructWithIndirectRefField2)) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}, targetFramework:=TargetFramework.Net90) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_Class() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Dim o As New Test + o.M(New Program()) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_StructWithManagedField() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Dim o As New Test + o.M(New S1()) + o.M(New S2()) + o.M(New S3()) + o.M(New S4(Of Integer)()) + o.M(New S4(Of Program)()) + o.M(New S5(Of Program)()) + End Sub +End Class + +Structure S1 + Dim F as Program +End Structure + +Structure S2 + Dim F as Integer +End Structure + +Structure S3 + Shared F as Program +End Structure + +Structure S4(Of T) + Dim F as T +End Structure + +Structure S5(Of T) + Dim F as Integer +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + + Dim s1 = compilation.GetTypeByMetadataName("S1") + Dim s2 = compilation.GetTypeByMetadataName("S2") + Dim s3 = compilation.GetTypeByMetadataName("S3") + Dim s4 = compilation.GetTypeByMetadataName("S4`1") + Dim s5 = compilation.GetTypeByMetadataName("S5`1") + Assert.Equal(ManagedKind.Managed, s1.GetManagedKind(Nothing)) + Assert.Equal(ManagedKind.Unmanaged, s2.GetManagedKind(Nothing)) + Assert.Equal(ManagedKind.Unmanaged, s3.GetManagedKind(Nothing)) + Assert.Equal(ManagedKind.Managed, s4.GetManagedKind(Nothing)) + Assert.Equal(ManagedKind.UnmanagedWithGenerics, s5.GetManagedKind(Nothing)) + + Assert.False(DirectCast(s1, INamedTypeSymbol).IsUnmanagedType) + Assert.True(DirectCast(s2, INamedTypeSymbol).IsUnmanagedType) + Assert.True(DirectCast(s3, INamedTypeSymbol).IsUnmanagedType) + Assert.False(DirectCast(s4, INamedTypeSymbol).IsUnmanagedType) + Assert.True(DirectCast(s5, INamedTypeSymbol).IsUnmanagedType) + + Dim s5T = s5.TypeParameters(0) + Assert.Equal(ManagedKind.Managed, s5T.GetManagedKind(Nothing)) + Assert.False(DirectCast(s5T, ITypeSymbol).IsUnmanagedType) + Assert.False(s5T.HasUnmanagedTypeConstraint) + Assert.False(DirectCast(s5T, ITypeParameterSymbol).HasUnmanagedTypeConstraint) + + Dim mT = compilation.GetTypeByMetadataName("Test").GetMember(Of MethodSymbol)("M").TypeParameters(0) + Assert.Equal(ManagedKind.Unmanaged, mT.GetManagedKind(Nothing)) + Assert.True(DirectCast(mT, ITypeSymbol).IsUnmanagedType) + Assert.True(mT.HasUnmanagedTypeConstraint) + Assert.True(DirectCast(mT, ITypeParameterSymbol).HasUnmanagedTypeConstraint) + End Sub + + + Public Sub UnmanagedCheck_StructWithManagedProperty() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Dim o As New Test + o.M(New S1()) + o.M(New S2()) + End Sub +End Class + +Structure S1 + Property F as Program +End Structure + +Structure S2 + Property F as Integer +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_StructWithEvent() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Dim o As New Test + o.M(New S1()) + End Sub +End Class + +Structure S1 + Event E as System.Action +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_StructWithCycle() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Dim o As New Test + o.M(New S1()) + o.M(New S2()) + End Sub +End Class + +Structure S1 + Dim F as S2 +End Structure + +Structure S2 + Dim F as S1 +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_Tuple() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main(Of T)(x As T) + Dim o As New Test + o.M((1, 1)) + o.M((1, x)) + o.M((1, ""s"")) + o.M((1, 2, 3, 4, 5, 6, 7, 8, ""s"")) + o.M(new S1()) + o.M(new S2()) + o.M(new S3()) + o.M(new S4(Of T)()) + End Sub +End Class + +Structure S1 + Dim F as (Integer, Integer) +End Structure + +Structure S2 + Dim F as (String, Integer) +End Structure + +Structure S3 + Dim F as (Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, String) +End Structure + +Structure S4(Of T) + Dim F as (T, Integer) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_Enum() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main() + Dim o As New Test + o.M(E1.Val) + o.M(new S1()) + End Sub +End Class + +Enum E1 + Val +End Enum + +Structure S1 + Dim F as E1 +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + compilation.AssertNoDiagnostics() + End Sub + + + Public Sub UnmanagedCheck_Interface() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main(x As I1) + Dim o As New Test + o.M(x) + o.M(New S2()) + End Sub +End Class + +Public Interface I1 +End Interface + +Structure S2 + Dim F as I1 +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_Pointer() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} + + public static S1 GetS1() => default; + public static S2 GetS2() => default; +} + +unsafe public struct S1 +{ + int* P1; +} + +unsafe public struct S2 +{ + delegate* P1; +} +", + parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest), + compilationOptions:=New CSharp.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithAllowUnsafe(True)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main() + Dim o As New Test + o.M(New S1()) + o.M(New S2()) + o.M(Test.GetS1()) + o.M(Test.GetS2()) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_01() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Dim field as YourStruct(Of MyStruct(Of MyStruct(Of T))) + Dim s as string +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_02() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Dim s as string + Dim field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_03() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Property field as YourStruct(Of MyStruct(Of MyStruct(Of T))) + Dim s as string +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_04() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Dim s as string + Property field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_05() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Event field as YourStruct(Of MyStruct(Of MyStruct(Of T))) + Dim s as string +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_06() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Dim s as string + Event field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_07() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; + string s; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Dim field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_08() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + string s; + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Dim field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_09() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; + string s; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Property field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_10() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + string s; + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Property field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_11() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + public T field; + string s; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Event field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_ExpandingTypeArgument_12() + Dim reference = CreateCSharpCompilation(" +public struct YourStruct where T : unmanaged +{ + string s; + public T field; +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +Structure MyStruct(Of T) + Event field as YourStruct(Of MyStruct(Of MyStruct(Of T))) +End Structure +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub UnmanagedCheck_TypeParameter() + Dim reference = CreateCSharpCompilation(" +public class Test +{ + public void M(T x) where T : unmanaged {} +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main(Of T1, T2 As Class, T3 As Structure)(x1 as T1, x2 As T2, x3 As T3) + Dim o As New Test + o.M(x1) + o.M(x2) + o.M(x3) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}) + AssertTheseDiagnostics(compilation, ) + End Sub + + + Public Sub ConsumeAssertEqual() + Dim reference = CreateCSharpCompilation(" +using System; + +public class Assert +{ + public static void Equal( + T[] expected, + T[] actual) + where T : unmanaged, IEquatable + { + System.Console.Write(""T[]""); + } + + public static void Equal( + T expected, + T actual) + { + System.Console.Write(""T""); + } +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Assert.Equal(New Integer() {1, 2}, New Integer() {1, 2}) + Assert.Equal(New String() {1, 2}, New String() {1, 2}) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}, options:=TestOptions.DebugExe) + CompileAndVerify(compilation, expectedOutput:="T[]T").VerifyDiagnostics() + + compilation = CreateCompilation(source, references:={reference}, options:=TestOptions.DebugExe, parseOptions:=TestOptions.Regular17_13) + CompileAndVerify(compilation, expectedOutput:="T[]T").VerifyDiagnostics() + + compilation = CreateCompilation(source, references:={reference}, options:=TestOptions.DebugExe, parseOptions:=TestOptions.Regular16_9) + compilation.AssertTheseDiagnostics( +BC36716: Visual Basic 16.9 does not support recognizing 'unmanaged' constraint. + Assert.Equal(New Integer() {1, 2}, New Integer() {1, 2}) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +BC36716: Visual Basic 16.9 does not support recognizing 'unmanaged' constraint. + Assert.Equal(New String() {1, 2}, New String() {1, 2}) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ) + End Sub + + + Public Sub ConsumeExtensionMethod() + Dim reference = CreateCSharpCompilation(" +using System; + +public static class Assert +{ + public static void Equal( + this T[] expected, + T[] actual) + where T : unmanaged, IEquatable + { + System.Console.Write(""T[]""); + } + + public static void Equal( + this T expected, + T actual) + { + System.Console.Write(""T""); + } +} +", parseOptions:=New CSharpParseOptions(CSharp.LanguageVersion.Latest)).EmitToImageReference() + + Dim source = " +public class Program + Shared Sub Main + Call New Integer() {1, 2}.Equal(New Integer() {1, 2}) + Call New String() {1, 2}.Equal(New String() {1, 2}) + End Sub +End Class +" + + Dim compilation = CreateCompilation(source, references:={reference}, options:=TestOptions.DebugExe) + CompileAndVerify(compilation, expectedOutput:="T[]T").VerifyDiagnostics() + + compilation = CreateCompilation(source, references:={reference}, options:=TestOptions.DebugExe, parseOptions:=TestOptions.Regular17_13) + CompileAndVerify(compilation, expectedOutput:="T[]T").VerifyDiagnostics() + + compilation = CreateCompilation(source, references:={reference}, options:=TestOptions.DebugExe, parseOptions:=TestOptions.Regular16_9) + CompileAndVerify(compilation, expectedOutput:="TT").VerifyDiagnostics() + End Sub + + Private Const IsUnmanagedAttributeIL As String = " +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) + .ver 4:0:0:0 +} +.assembly Test +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module Test.dll +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 +.corflags 0x00000001 + +.class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsUnmanagedAttribute + extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Attribute::.ctor() + IL_0006: nop + IL_0007: ret + } +} +" + + End Class +End Namespace diff --git a/src/Dependencies/Collections/Internal/SegmentedArrayHelper.cs b/src/Dependencies/Collections/Internal/SegmentedArrayHelper.cs index c9ca2d2d31f19..87caa4c74b574 100644 --- a/src/Dependencies/Collections/Internal/SegmentedArrayHelper.cs +++ b/src/Dependencies/Collections/Internal/SegmentedArrayHelper.cs @@ -23,6 +23,8 @@ internal static int GetSegmentSize() // Hard code common values since not all versions of the .NET JIT support reducing this computation to a // constant value at runtime. Values are validated against the reference implementation in // CalculateSegmentSize in unit tests. + 1 => 65536, + 2 => 32768, 4 => 16384, 8 => 8192, 12 => 4096, @@ -31,6 +33,7 @@ internal static int GetSegmentSize() 28 => 2048, 32 => 2048, 40 => 2048, + 64 => 1024, #if NETCOREAPP3_0_OR_GREATER _ => InlineCalculateSegmentSize(Unsafe.SizeOf()), #else @@ -47,6 +50,8 @@ internal static int GetSegmentShift() // Hard code common values since not all versions of the .NET JIT support reducing this computation to a // constant value at runtime. Values are validated against the reference implementation in // CalculateSegmentSize in unit tests. + 1 => 16, + 2 => 15, 4 => 14, 8 => 13, 12 => 12, @@ -55,6 +60,7 @@ internal static int GetSegmentShift() 28 => 11, 32 => 11, 40 => 11, + 64 => 10, #if NETCOREAPP3_0_OR_GREATER _ => InlineCalculateSegmentShift(Unsafe.SizeOf()), #else @@ -71,6 +77,8 @@ internal static int GetOffsetMask() // Hard code common values since not all versions of the .NET JIT support reducing this computation to a // constant value at runtime. Values are validated against the reference implementation in // CalculateSegmentSize in unit tests. + 1 => 65535, + 2 => 32767, 4 => 16383, 8 => 8191, 12 => 4095, @@ -79,6 +87,7 @@ internal static int GetOffsetMask() 28 => 2047, 32 => 2047, 40 => 2047, + 64 => 1023, #if NETCOREAPP3_0_OR_GREATER _ => InlineCalculateOffsetMask(Unsafe.SizeOf()), #else diff --git a/src/Dependencies/Collections/SegmentedArray`1+PrivateMarshal.cs b/src/Dependencies/Collections/SegmentedArray`1+PrivateMarshal.cs index d4ad3359886dc..20433a3451bda 100644 --- a/src/Dependencies/Collections/SegmentedArray`1+PrivateMarshal.cs +++ b/src/Dependencies/Collections/SegmentedArray`1+PrivateMarshal.cs @@ -14,5 +14,8 @@ internal static class PrivateMarshal /// public static T[][] AsSegments(SegmentedArray array) => array._items; + + public static SegmentedArray AsSegmentedArray(int length, T[][] segments) + => new SegmentedArray(length, segments); } } diff --git a/src/Dependencies/Collections/SegmentedCollectionsMarshal.cs b/src/Dependencies/Collections/SegmentedCollectionsMarshal.cs index cb4c90b3f8c53..d8a9ebeef1ab6 100644 --- a/src/Dependencies/Collections/SegmentedCollectionsMarshal.cs +++ b/src/Dependencies/Collections/SegmentedCollectionsMarshal.cs @@ -22,6 +22,25 @@ internal static class SegmentedCollectionsMarshal public static T[][] AsSegments(SegmentedArray array) => SegmentedArray.PrivateMarshal.AsSegments(array); + /// + /// Gets a value wrapping the input T[][]. + /// + /// The type of elements in the input. + /// The combined length of the input arrays + /// The input array to wrap in the returned value. + /// A value wrapping . + /// + /// + /// When using this method, callers should take extra care to ensure that they're the sole owners of the input + /// array, and that it won't be modified once the returned value starts + /// being used. Doing so might cause undefined behavior in code paths which don't expect the contents of a given + /// values to change outside their control. + /// + /// + /// Thrown when is + public static SegmentedArray AsSegmentedArray(int length, T[][] segments) + => SegmentedArray.PrivateMarshal.AsSegmentedArray(length, segments); + /// /// Gets either a ref to a in the or a /// ref null if it does not exist in the . diff --git a/src/Dependencies/Collections/SegmentedDictionary`2.cs b/src/Dependencies/Collections/SegmentedDictionary`2.cs index 6a46b472a4cc8..75834b2f50781 100644 --- a/src/Dependencies/Collections/SegmentedDictionary`2.cs +++ b/src/Dependencies/Collections/SegmentedDictionary`2.cs @@ -646,10 +646,10 @@ private void Resize(int newSize) Debug.Assert(_entries.Length > 0, "_entries should be non-empty"); Debug.Assert(newSize >= _entries.Length); - var entries = new SegmentedArray(newSize); - var count = _count; - SegmentedArray.Copy(_entries, entries, count); + + // Rather than creating a copy of _entries, instead reuse as much of it's data as possible. + var entries = CreateNewSegmentedArrayReusingOldSegments(_entries, newSize); // Assign member variables after both arrays allocated to guard against corruption from OOM if second fails _buckets = new SegmentedArray(newSize); @@ -667,6 +667,28 @@ private void Resize(int newSize) _entries = entries; } + private static SegmentedArray CreateNewSegmentedArrayReusingOldSegments(SegmentedArray oldArray, int newSize) + { + var segments = SegmentedCollectionsMarshal.AsSegments(oldArray); + + var oldSegmentCount = segments.Length; + var newSegmentCount = (newSize + SegmentedArrayHelper.GetSegmentSize() - 1) >> SegmentedArrayHelper.GetSegmentShift(); + + // Grow the array of segments, if necessary + Array.Resize(ref segments, newSegmentCount); + + // Resize all segments to full segment size from the last old segment to the next to last + // new segment. + for (var i = oldSegmentCount - 1; i < newSegmentCount - 1; i++) + Array.Resize(ref segments[i], SegmentedArrayHelper.GetSegmentSize()); + + // Resize the last segment + var lastSegmentSize = newSize - ((newSegmentCount - 1) << SegmentedArrayHelper.GetSegmentShift()); + Array.Resize(ref segments[newSegmentCount - 1], lastSegmentSize); + + return SegmentedCollectionsMarshal.AsSegmentedArray(newSize, segments); + } + public bool Remove(TKey key) { // The overload Remove(TKey key, out TValue value) is a copy of this method with one additional diff --git a/src/Dependencies/Collections/SegmentedHashSet`1.cs b/src/Dependencies/Collections/SegmentedHashSet`1.cs index c8792b3e9f5ed..080048041e5ba 100644 --- a/src/Dependencies/Collections/SegmentedHashSet`1.cs +++ b/src/Dependencies/Collections/SegmentedHashSet`1.cs @@ -899,10 +899,10 @@ private void Resize(int newSize) Debug.Assert(_entries.Length > 0, "_entries should be non-empty"); Debug.Assert(newSize >= _entries.Length); - var entries = new SegmentedArray(newSize); - var count = _count; - SegmentedArray.Copy(_entries, entries, count); + + // Rather than creating a copy of _entries, instead reuse as much of it's data as possible. + var entries = CreateNewSegmentedArrayReusingOldSegments(_entries, newSize); // Assign member variables after both arrays allocated to guard against corruption from OOM if second fails _buckets = new SegmentedArray(newSize); @@ -921,6 +921,28 @@ private void Resize(int newSize) _entries = entries; } + private static SegmentedArray CreateNewSegmentedArrayReusingOldSegments(SegmentedArray oldArray, int newSize) + { + var segments = SegmentedCollectionsMarshal.AsSegments(oldArray); + + var oldSegmentCount = segments.Length; + var newSegmentCount = (newSize + SegmentedArrayHelper.GetSegmentSize() - 1) >> SegmentedArrayHelper.GetSegmentShift(); + + // Grow the array of segments, if necessary + Array.Resize(ref segments, newSegmentCount); + + // Resize all segments to full segment size from the last old segment to the next to last + // new segment. + for (var i = oldSegmentCount - 1; i < newSegmentCount - 1; i++) + Array.Resize(ref segments[i], SegmentedArrayHelper.GetSegmentSize()); + + // Resize the last segment + var lastSegmentSize = newSize - ((newSegmentCount - 1) << SegmentedArrayHelper.GetSegmentShift()); + Array.Resize(ref segments[newSegmentCount - 1], lastSegmentSize); + + return SegmentedCollectionsMarshal.AsSegmentedArray(newSize, segments); + } + /// /// Sets the capacity of a object to the actual number of elements it contains, /// rounded up to a nearby, implementation-specific value. diff --git a/src/Dependencies/Collections/SegmentedList`1.cs b/src/Dependencies/Collections/SegmentedList`1.cs index 4a5604cbefb39..35118b06966ad 100644 --- a/src/Dependencies/Collections/SegmentedList`1.cs +++ b/src/Dependencies/Collections/SegmentedList`1.cs @@ -131,25 +131,50 @@ public int Capacity ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity); } - if (value != _items.Length) + if (value == _items.Length) + return; + + if (value <= 0) { - if (value > 0) - { - var newItems = new SegmentedArray(value); - if (_size > 0) - { - SegmentedArray.Copy(_items, newItems, _size); - } - _items = newItems; - } - else - { - _items = s_emptyArray; - } + _items = s_emptyArray; + return; + } + + if (_items.Length == 0) + { + // No data from existing array to reuse, just create a new one. + _items = new SegmentedArray(value); + } + else + { + // Rather than creating a copy of _items, instead reuse as much of it's data as possible. + _items = CreateNewSegmentedArrayReusingOldSegments(_items, value); } } } + private static SegmentedArray CreateNewSegmentedArrayReusingOldSegments(SegmentedArray oldArray, int newSize) + { + var segments = SegmentedCollectionsMarshal.AsSegments(oldArray); + + var oldSegmentCount = segments.Length; + var newSegmentCount = (newSize + SegmentedArrayHelper.GetSegmentSize() - 1) >> SegmentedArrayHelper.GetSegmentShift(); + + // Grow the array of segments, if necessary + Array.Resize(ref segments, newSegmentCount); + + // Resize all segments to full segment size from the last old segment to the next to last + // new segment. + for (var i = oldSegmentCount - 1; i < newSegmentCount - 1; i++) + Array.Resize(ref segments[i], SegmentedArrayHelper.GetSegmentSize()); + + // Resize the last segment + var lastSegmentSize = newSize - ((newSegmentCount - 1) << SegmentedArrayHelper.GetSegmentShift()); + Array.Resize(ref segments[newSegmentCount - 1], lastSegmentSize); + + return SegmentedCollectionsMarshal.AsSegmentedArray(newSize, segments); + } + // Read-only property describing how many elements are in the SegmentedList. public int Count => _size; @@ -492,18 +517,54 @@ internal void Grow(int capacity) { Debug.Assert(_items.Length < capacity); - var newCapacity = _items.Length == 0 ? DefaultCapacity : 2 * _items.Length; + var newCapacity = 0; - // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. - // Note that this check works even when _items.Length overflowed thanks to the (uint) cast - if ((uint)newCapacity > MaxLength) - newCapacity = MaxLength; + if (_items.Length < SegmentedArrayHelper.GetSegmentSize() / 2) + { + // The array isn't near the maximum segment size. If the array is empty, the new capacity + // should be DefaultCapacity. Otherwise, the new capacity should be double the current array size. + newCapacity = _items.Length == 0 ? DefaultCapacity : _items.Length * 2; + } + else if (_items.Length < SegmentedArrayHelper.GetSegmentSize()) + { + // There is only a single segment that is over half full. Increase it to a full segment. + newCapacity = SegmentedArrayHelper.GetSegmentSize(); + } + else + { + // If the last segment is fully sized, increase the number of segments by the desired growth rate + if (0 == (_items.Length & SegmentedArrayHelper.GetOffsetMask())) + { + // This value determines the growth rate of the number of segments to use. + // For a value of 3, this means the segment count will grow at a rate of + // 1 + (1 >> 3) or 12.5% + const int segmentGrowthShiftValue = 3; + + var oldSegmentCount = (_items.Length + SegmentedArrayHelper.GetSegmentSize() - 1) >> SegmentedArrayHelper.GetSegmentShift(); + var newSegmentCount = oldSegmentCount + Math.Max(1, oldSegmentCount >> segmentGrowthShiftValue); + + newCapacity = SegmentedArrayHelper.GetSegmentSize() * newSegmentCount; + } + } - // If the computed capacity is still less than specified, set to the original argument. + // If the computed capacity is less than specified, set to the original argument. // Capacities exceeding Array.MaxLength will be surfaced as OutOfMemoryException by Array.Resize. if (newCapacity < capacity) newCapacity = capacity; + if (newCapacity > SegmentedArrayHelper.GetSegmentSize()) + { + // If the last segment isn't fully sized, increase the new capacity such that it will be. + var lastSegmentLength = newCapacity & SegmentedArrayHelper.GetOffsetMask(); + if (lastSegmentLength > 0) + newCapacity = (newCapacity - lastSegmentLength) + SegmentedArrayHelper.GetSegmentSize(); + + // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. + // Note that this check works even when _items.Length overflowed thanks to the (uint) cast + if ((uint)newCapacity > MaxLength) + newCapacity = MaxLength; + } + Capacity = newCapacity; } diff --git a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs index 9636bd3fbb871..0341f9fbbce32 100644 --- a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs +++ b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs @@ -32,9 +32,6 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.AutomaticCompletion; -/// -/// csharp automatic line ender command handler -/// [Export(typeof(ICommandHandler))] [ContentType(ContentTypeNames.CSharpContentType)] [Name(PredefinedCommandHandlerNames.AutomaticLineEnder)] @@ -324,18 +321,18 @@ protected override void ModifySelectedNode( { // For these syntax node, braces pair could be easily added by modify the syntax tree if (selectedNode is BaseTypeDeclarationSyntax - or BaseMethodDeclarationSyntax - or LocalFunctionStatementSyntax - or AccessorDeclarationSyntax - or ObjectCreationExpressionSyntax - or WhileStatementSyntax - or ForEachStatementSyntax - or ForStatementSyntax - or LockStatementSyntax - or UsingStatementSyntax - or DoStatementSyntax - or IfStatementSyntax - or ElseClauseSyntax) + or BaseMethodDeclarationSyntax + or LocalFunctionStatementSyntax + or AccessorDeclarationSyntax + or ObjectCreationExpressionSyntax + or WhileStatementSyntax + or CommonForEachStatementSyntax + or ForStatementSyntax + or LockStatementSyntax + or UsingStatementSyntax + or DoStatementSyntax + or IfStatementSyntax + or ElseClauseSyntax) { // Add the braces and get the next caretPosition var (newRoot, nextCaretPosition) = AddBraceToSelectedNode(document.SolutionServices, document.Root, selectedNode, formattingOptions, cancellationToken); diff --git a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler_Helpers.cs b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler_Helpers.cs index 4986a0098536b..26c858636746f 100644 --- a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler_Helpers.cs +++ b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler_Helpers.cs @@ -942,7 +942,7 @@ private static SyntaxNode AddBlockToEmbeddedStatementOwner( return embeddedStatementOwner switch { DoStatementSyntax doStatementNode => doStatementNode.WithStatement(block), - ForEachStatementSyntax forEachStatementNode => forEachStatementNode.WithStatement(block), + CommonForEachStatementSyntax forEachStatementNode => forEachStatementNode.WithStatement(block), ForStatementSyntax forStatementNode => forStatementNode.WithStatement(block), IfStatementSyntax ifStatementNode => ifStatementNode.WithStatement(block), ElseClauseSyntax elseClauseNode => elseClauseNode.WithStatement(block), diff --git a/src/EditorFeatures/CSharp/Formatting/CSharpFormattingInteractionService.cs b/src/EditorFeatures/CSharp/Formatting/CSharpFormattingInteractionService.cs index c51e8dd9b7c3f..538b8250aaaae 100644 --- a/src/EditorFeatures/CSharp/Formatting/CSharpFormattingInteractionService.cs +++ b/src/EditorFeatures/CSharp/Formatting/CSharpFormattingInteractionService.cs @@ -5,10 +5,8 @@ using System; using System.Collections.Immutable; using System.Composition; -using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Indentation; @@ -24,10 +22,17 @@ namespace Microsoft.CodeAnalysis.CSharp.Formatting; [ExportLanguageService(typeof(IFormattingInteractionService), LanguageNames.CSharp), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal partial class CSharpFormattingInteractionService(EditorOptionsService editorOptionsService) : IFormattingInteractionService +internal sealed class CSharpFormattingInteractionService(EditorOptionsService editorOptionsService) : IFormattingInteractionService { - // All the characters that might potentially trigger formatting when typed - private static readonly char[] _supportedChars = ";{}#nte:)".ToCharArray(); + /// + /// All the characters that might potentially trigger formatting when typed. The punctuation characters are the + /// normal C# punctuation characters that start/end constructs we want to format when starting/ending them. The + /// letters 'n', 't', and 'e' are the ending characters of certain identifiers that we want to format when they are + /// completely written. For example, if the user types # we left align the preprocessor directive + /// immediately. However, once they type region (which ends with 'n') we then want to indent it. See for those identifier cases. + /// + private const string s_supportedChars = ";{}#:)nte"; private readonly EditorOptionsService _editorOptionsService = editorOptionsService; @@ -47,36 +52,26 @@ public bool SupportsFormattingOnTypedCharacter(Document document, char ch) // // See extended comment in GetFormattingChangesAsync for more details on this. if (isSmartIndent && ch is '{' or '}') - { return true; - } var options = _editorOptionsService.GlobalOptions.GetAutoFormattingOptions(LanguageNames.CSharp); // If format-on-typing is not on, then we don't support formatting on any other characters. var autoFormattingOnTyping = options.FormatOnTyping; if (!autoFormattingOnTyping) - { return false; - } if (ch == '}' && !options.FormatOnCloseBrace) - { return false; - } if (ch == ';' && !options.FormatOnSemicolon) - { return false; - } // don't auto format after these keys if smart indenting is not on. if (ch is '#' or 'n' && !isSmartIndent) - { return false; - } - return _supportedChars.Contains(ch); + return s_supportedChars.IndexOf(ch) >= 0; } public Task> GetFormattingChangesAsync( diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index 0c66d57a8fa48..22c871fa7b1b8 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Indentation; @@ -50,74 +51,197 @@ public bool ExecuteCommand(ReturnKeyCommandArgs args, CommandExecutionContext co if (position >= currentSnapshot.Length) return false; - if (currentSnapshot[position] != '"') + var cancellationToken = context.OperationContext.UserCancellationToken; + + var document = currentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); + if (document == null) return false; - var quotesBefore = 0; - var quotesAfter = 0; + return currentSnapshot[position] == '"' + ? ExecuteReturnCommandBeforeQuoteCharacter() + : ExecuteReturnCommandNotBeforeQuoteCharacter(); - for (int i = position, n = currentSnapshot.Length; i < n; i++) + bool ExecuteReturnCommandBeforeQuoteCharacter() { - if (currentSnapshot[i] != '"') - break; - - quotesAfter++; + var quotesBefore = 0; + var quotesAfter = 0; + + // Ensure we're in between a balanced set of quotes, with at least 3 quotes on each side. + + var currentSnapshot = subjectBuffer.CurrentSnapshot; + for (int i = position, n = currentSnapshot.Length; i < n; i++) + { + if (currentSnapshot[i] != '"') + break; + + quotesAfter++; + } + + for (var i = position - 1; i >= 0; i--) + { + if (currentSnapshot[i] != '"') + break; + + quotesBefore++; + } + + // We support two cases here. Something simple like `"""$$"""`. In this case, we have to be hitting enter + // inside balanced quotes. But we also support `"""goo$$"""`. In this case it's ok if quotes are not + // balanced. We're going to go through the non-empty path involving adding multiple newlines to the final + // text. + + var isEmpty = quotesBefore > 0; + if (isEmpty && quotesAfter != quotesBefore) + return false; + + if (quotesAfter < 3) + return false; + + // Looks promising based on text alone. Now ensure we're actually on a raw string token/expression. + var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); + + var token = parsedDocument.Root.FindToken(position); + if (token.Kind() is not (SyntaxKind.SingleLineRawStringLiteralToken or + SyntaxKind.MultiLineRawStringLiteralToken or + SyntaxKind.InterpolatedSingleLineRawStringStartToken or + SyntaxKind.InterpolatedMultiLineRawStringStartToken) || + token.Parent is not ExpressionSyntax expression) + { + return false; + } + + return MakeEdit(parsedDocument, expression, isEmpty); } - for (var i = position - 1; i >= 0; i--) + bool ExecuteReturnCommandNotBeforeQuoteCharacter() { - if (currentSnapshot[i] != '"') - break; - - quotesBefore++; + // If the caret is not on a quote, we need to find whether we are within the contents of a single-line raw + // string literal but not inside an interpolation. If we are inside a raw string literal and the caret is not on + // top of a quote, it is part of the literal's text. Here we try to ensure that the literal's closing quotes are + // properly placed in their own line We could reach this point after pressing enter within a single-line raw + // string + + var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); + + var token = parsedDocument.Root.FindToken(position); + ExpressionSyntax expression; + switch (token.Kind()) + { + case SyntaxKind.SingleLineRawStringLiteralToken when token.Parent is ExpressionSyntax parentExpression: + expression = parentExpression; + break; + + case SyntaxKind.InterpolatedStringTextToken: + case SyntaxKind.OpenBraceToken: + if (token is not + { + Parent.Parent: InterpolatedStringExpressionSyntax + { + StringStartToken.RawKind: (int)SyntaxKind.InterpolatedSingleLineRawStringStartToken, + } interpolatedStringExpression, + }) + { + return false; + } + + if (token.Kind() is SyntaxKind.OpenBraceToken && position != token.SpanStart) + return false; + + expression = interpolatedStringExpression; + break; + + default: + return false; + } + + return MakeEdit(parsedDocument, expression, isEmpty: false); } - if (quotesAfter != quotesBefore) - return false; - - if (quotesAfter < 3) - return false; - - return SplitRawString(textView, subjectBuffer, span.Start.Position, CancellationToken.None); - } - - private bool SplitRawString(ITextView textView, ITextBuffer subjectBuffer, int position, CancellationToken cancellationToken) - { - var document = subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); - if (document == null) - return false; - - var parsedDocument = ParsedDocument.CreateSynchronously(document, cancellationToken); - - var token = parsedDocument.Root.FindToken(position); - if (token.Kind() is not (SyntaxKind.SingleLineRawStringLiteralToken or - SyntaxKind.MultiLineRawStringLiteralToken or - SyntaxKind.InterpolatedSingleLineRawStringStartToken or - SyntaxKind.InterpolatedMultiLineRawStringStartToken)) + bool MakeEdit( + ParsedDocument parsedDocument, + ExpressionSyntax expression, + bool isEmpty) { - return false; + var project = document.Project; + var indentationOptions = subjectBuffer.GetIndentationOptions(_editorOptionsService, project.GetFallbackAnalyzerOptions(), project.Services, explicitFormat: false); + var indentation = expression.GetFirstToken().GetPreferredIndentation(parsedDocument, indentationOptions, cancellationToken); + + var newLine = indentationOptions.FormattingOptions.NewLine; + + using var transaction = CaretPreservingEditTransaction.TryCreate( + CSharpEditorResources.Split_raw_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService); + var edit = subjectBuffer.CreateEdit(); + + if (isEmpty) + { + // If the literal is empty, we just want to help the user transform it into a multiline raw string + // literal with the extra empty newline between the delimiters to place the caret at + edit.Insert(position, newLine + newLine + indentation); + + var finalSnapshot = edit.Apply(); + + // move caret to the right location in virtual space for the blank line we added. + var lineInNewSnapshot = finalSnapshot.GetLineFromPosition(position); + var nextLine = finalSnapshot.GetLineFromLineNumber(lineInNewSnapshot.LineNumber + 1); + textView.Caret.MoveTo(new VirtualSnapshotPoint(nextLine, indentation.Length)); + } + else + { + // Otherwise, we're starting with a raw string that has content in it. That's something like: + // """GooBar""". If we hit enter at the `G` we only want to insert a single new line before the caret. + // However, if we were to hit enter anywhere after that, we want two new lines inserted. One after the + // `"""` and one at the caret itself. + var newLineAndIndentation = newLine + indentation; + + // Add a newline at the position of the end literal + var closingStart = GetStartPositionOfClosingDelimiter(expression); + edit.Insert(closingStart, newLineAndIndentation); + + // Add a newline at the caret's position, to insert the newline that the user requested + edit.Insert(position, newLineAndIndentation); + + // Also add a newline at the start of the text, only if there is text before the caret's position + var insertedLinesBeforeCaret = 1; + var openingEnd = GetEndPositionOfOpeningDelimiter(expression); + if (openingEnd != position) + { + insertedLinesBeforeCaret = 2; + edit.Insert(openingEnd, newLineAndIndentation); + } + + var finalSnapshot = edit.Apply(); + + var lineInNewSnapshot = finalSnapshot.GetLineFromPosition(openingEnd); + var nextLine = finalSnapshot.GetLineFromLineNumber(lineInNewSnapshot.LineNumber + insertedLinesBeforeCaret); + textView.Caret.MoveTo(new VirtualSnapshotPoint(nextLine, indentation.Length)); + } + + transaction?.Complete(); + return true; } - var indentationOptions = subjectBuffer.GetIndentationOptions(_editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: false); - var indentation = token.GetPreferredIndentation(parsedDocument, indentationOptions, cancellationToken); - - var newLine = indentationOptions.FormattingOptions.NewLine; + int GetStartPositionOfClosingDelimiter(ExpressionSyntax expression) + { + if (expression is InterpolatedStringExpressionSyntax interpolatedStringExpression) + return interpolatedStringExpression.StringEndToken.Span.Start; - using var transaction = CaretPreservingEditTransaction.TryCreate( - CSharpEditorResources.Split_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService); + var index = expression.Span.End; + while (currentSnapshot[index - 1] == '"') + index--; - var edit = subjectBuffer.CreateEdit(); + return index; + } - // apply the change: - edit.Insert(position, newLine + newLine + indentation); - var snapshot = edit.Apply(); + int GetEndPositionOfOpeningDelimiter(ExpressionSyntax expression) + { + if (expression is InterpolatedStringExpressionSyntax interpolatedStringExpression) + return interpolatedStringExpression.StringStartToken.Span.End; - // move caret: - var lineInNewSnapshot = snapshot.GetLineFromPosition(position); - var nextLine = snapshot.GetLineFromLineNumber(lineInNewSnapshot.LineNumber + 1); - textView.Caret.MoveTo(new VirtualSnapshotPoint(nextLine, indentation.Length)); + var index = expression.Span.Start; + while (currentSnapshot[index] == '"') + index++; - transaction?.Complete(); - return true; + return index; + } } } diff --git a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs index d795151cdd000..c3b01029400f0 100644 --- a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs +++ b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLineEnderTests.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using Microsoft.CodeAnalysis.Editor.CSharp.AutomaticCompletion; using Microsoft.CodeAnalysis.Editor.UnitTests.AutomaticCompletion; @@ -16,862 +14,1087 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.AutomaticCompletion; [Trait(Traits.Feature, Traits.Features.AutomaticCompletion)] -public class AutomaticLineEnderTests : AbstractAutomaticLineEnderTests +public sealed class AutomaticLineEnderTests : AbstractAutomaticLineEnderTests { [WpfFact] public void Creation() { - Test(@" -$$", "$$"); + Test(""" + + $$ + """, "$$"); } [WpfFact] public void Usings() { - Test(@"using System; -$$", @"using System$$"); + Test(""" + using System; + $$ + """, @"using System$$"); } [WpfFact] public void Namespace() { - Test(@"namespace {} -$$", @"namespace {$$}"); + Test(""" + namespace {} + $$ + """, @"namespace {$$}"); } [WpfFact] public void Class() { - Test(@"class {} -$$", "class {$$}"); + Test(""" + class {} + $$ + """, "class {$$}"); } [WpfFact] public void Method() { - Test(@"class C -{ - void Method() {$$} -}", @"class C -{ - void Method() {$$} -}", assertNextHandlerInvoked: true); + Test(""" + class C + { + void Method() {$$} + } + """, """ + class C + { + void Method() {$$} + } + """, assertNextHandlerInvoked: true); } [WpfFact] public void Field() { - Test(@"class C -{ - private readonly int i = 3; - $$ -}", @"class C -{ - pri$$vate re$$adonly i$$nt i = 3$$ -}"); + Test(""" + class C + { + private readonly int i = 3; + $$ + } + """, """ + class C + { + pri$$vate re$$adonly i$$nt i = 3$$ + } + """); } [WpfFact] public void EventField() { - Test(@"class C -{ - event System.EventHandler e = null; - $$ -}", @"class C -{ - e$$vent System.Even$$tHandler e$$ = null$$ -}"); + Test(""" + class C + { + event System.EventHandler e = null; + $$ + } + """, """ + class C + { + e$$vent System.Even$$tHandler e$$ = null$$ + } + """); } [WpfFact] public void Field2() { - Test(@"class C -{ - private readonly int i; - $$ -}", @"class C -{ - private readonly int i$$ -}"); + Test(""" + class C + { + private readonly int i; + $$ + } + """, """ + class C + { + private readonly int i$$ + } + """); } [WpfFact] public void EventField2() { - Test(@"class C -{ - event System.EventHandler e - { - $$ - } -}", @"class C -{ - eve$$nt System.E$$ventHandler e$$ -}"); + Test(""" + class C + { + event System.EventHandler e + { + $$ + } + } + """, """ + class C + { + eve$$nt System.E$$ventHandler e$$ + } + """); } [WpfFact] public void Field3() { - Test(@"class C -{ - private readonly int - $$ -}", @"class C -{ - private readonly int$$ -}"); + Test(""" + class C + { + private readonly int + $$ + } + """, """ + class C + { + private readonly int$$ + } + """); } [WpfFact] public void EventField3() { - Test(@"class C -{ - event System.EventHandler - $$ -}", @"class C -{ - event System.EventHandler$$ -}"); + Test(""" + class C + { + event System.EventHandler + $$ + } + """, """ + class C + { + event System.EventHandler$$ + } + """); } [WpfFact] - public void EmbededStatement() + public void EmbeddedStatement() { - Test(@"class C -{ - void Method() - { - if (true) - { - $$ - } - } -}", @"class C -{ - void Method() - { - if (true) $$ - } -}"); + Test(""" + class C + { + void Method() + { + if (true) + { + $$ + } + } + } + """, """ + class C + { + void Method() + { + if (true) $$ + } + } + """); } [WpfFact] - public void EmbededStatement1() - { - Test(@"class C -{ - void Method() - { - if (true) - Console.WriteLine() - $$ - } -}", @"class C -{ - void Method() + public void EmbeddedStatement1() { - if (true) - Console.WriteLine()$$ - } -}"); + Test(""" + class C + { + void Method() + { + if (true) + Console.WriteLine() + $$ + } + } + """, """ + class C + { + void Method() + { + if (true) + Console.WriteLine()$$ + } + } + """); } [WpfFact] - public void EmbededStatement2() - { - Test(@"class C -{ - void Method() - { - if (true) - Console.WriteLine(); - $$ - } -}", @"class C -{ - void Method() + public void EmbeddedStatement2() { - if (true) - Console.WriteLine($$) - } -}"); + Test(""" + class C + { + void Method() + { + if (true) + Console.WriteLine(); + $$ + } + } + """, """ + class C + { + void Method() + { + if (true) + Console.WriteLine($$) + } + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/57323")] - public void EmbededStatementFollowedByStatement() - { - Test(@"class C -{ - void Method() - { - if (true) - { - } - if (true) - { - $$ - } - if (true) - { - } - } -}", @"class C -{ - void Method() + public void EmbeddedStatementFollowedByStatement() { - if (true) - { - } - if (true$$) - if (true) - { - } - } -}"); + Test(""" + class C + { + void Method() + { + if (true) + { + } + if (true) + { + $$ + } + if (true) + { + } + } + } + """, """ + class C + { + void Method() + { + if (true) + { + } + if (true$$) + if (true) + { + } + } + } + """); } [WpfFact] public void Statement() { - Test(@"class C -{ - void Method() - { - int i; - $$ - } -}", @"class C -{ - void Method() - { - int i$$ - } -}"); + Test(""" + class C + { + void Method() + { + int i; + $$ + } + } + """, """ + class C + { + void Method() + { + int i$$ + } + } + """); } [WpfFact] public void Statement1() { - Test(@"class C -{ - void Method() - { - int - $$ - } -}", @"class C -{ - void Method() - { - int$$ - } -}"); + Test(""" + class C + { + void Method() + { + int + $$ + } + } + """, """ + class C + { + void Method() + { + int$$ + } + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedMethod() { - Test(@"class T -{ - int M() => 1 + 2; - $$ -}", @"class T -{ - int M() => 1 + 2$$ -}"); + Test(""" + class T + { + int M() => 1 + 2; + $$ + } + """, """ + class T + { + int M() => 1 + 2$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedOperator() { - Test(@"class Complex -{ - int real; int imaginary; - public static Complex operator +(Complex a, Complex b) => a.Add(b.real + 1); - $$ - private Complex Add(int b) => null; -}", @"class Complex -{ - int real; int imaginary; - public static Complex operator +(Complex a, Complex b) => a.Add(b.real + 1)$$ - private Complex Add(int b) => null; -}"); + Test(""" + class Complex + { + int real; int imaginary; + public static Complex operator +(Complex a, Complex b) => a.Add(b.real + 1); + $$ + private Complex Add(int b) => null; + } + """, """ + class Complex + { + int real; int imaginary; + public static Complex operator +(Complex a, Complex b) => a.Add(b.real + 1)$$ + private Complex Add(int b) => null; + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedConversionOperator() { - Test(@"using System; -public struct DBBool -{ - public static readonly DBBool dbFalse = new DBBool(-1); - int value; + Test(""" + using System; + public struct DBBool + { + public static readonly DBBool dbFalse = new DBBool(-1); + int value; - DBBool(int value) - { - this.value = value; - } + DBBool(int value) + { + this.value = value; + } - public static implicit operator DBBool(bool x) => x ? new DBBool(1) : dbFalse; - $$ -}", @"using System; -public struct DBBool -{ - public static readonly DBBool dbFalse = new DBBool(-1); - int value; + public static implicit operator DBBool(bool x) => x ? new DBBool(1) : dbFalse; + $$ + } + """, """ + using System; + public struct DBBool + { + public static readonly DBBool dbFalse = new DBBool(-1); + int value; - DBBool(int value) - { - this.value = value; - } + DBBool(int value) + { + this.value = value; + } - public static implicit operator DBBool(bool x) => x ? new DBBool(1) : dbFalse$$ -}"); + public static implicit operator DBBool(bool x) => x ? new DBBool(1) : dbFalse$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedProperty() { - Test(@"class T -{ - int P1 => 1 + 2; - $$ -}", @"class T -{ - int P1 => 1 + 2$$ -}"); + Test(""" + class T + { + int P1 => 1 + 2; + $$ + } + """, """ + class T + { + int P1 => 1 + 2$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedIndexer() { - Test(@"using System; -class SampleCollection -{ - private T[] arr = new T[100]; - public T this[int i] => i > 0 ? arr[i + 1] : arr[i + 2]; - $$ -}", @"using System; -class SampleCollection -{ - private T[] arr = new T[100]; - public T this[int i] => i > 0 ? arr[i + 1] : arr[i + 2]$$ -}"); + Test(""" + using System; + class SampleCollection + { + private T[] arr = new T[100]; + public T this[int i] => i > 0 ? arr[i + 1] : arr[i + 2]; + $$ + } + """, """ + using System; + class SampleCollection + { + private T[] arr = new T[100]; + public T this[int i] => i > 0 ? arr[i + 1] : arr[i + 2]$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedMethodWithBlockBodiedAnonymousMethodExpression() { - Test(@"using System; -class TestClass -{ - Func Y() => delegate (int x) - { - return 9; - }; - $$ -}", @"using System; -class TestClass -{ - Func Y() => delegate (int x) - { - return 9; - }$$ -}"); + Test(""" + using System; + class TestClass + { + Func Y() => delegate (int x) + { + return 9; + }; + $$ + } + """, """ + using System; + class TestClass + { + Func Y() => delegate (int x) + { + return 9; + }$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedMethodWithSingleLineBlockBodiedAnonymousMethodExpression() { - Test(@"using System; -class TestClass -{ - Func Y() => delegate (int x) { return 9; }; - $$ -}", @"using System; -class TestClass -{ - Func Y() => delegate (int x) { return 9; }$$ -}"); + Test(""" + using System; + class TestClass + { + Func Y() => delegate (int x) { return 9; }; + $$ + } + """, """ + using System; + class TestClass + { + Func Y() => delegate (int x) { return 9; }$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedMethodWithBlockBodiedSimpleLambdaExpression() { - Test(@"using System; -class TestClass -{ - Func Y() => f => - { - return f * 9; - }; - $$ -}", @"using System; -class TestClass -{ - Func Y() => f => - { - return f * 9; - }$$ -}"); + Test(""" + using System; + class TestClass + { + Func Y() => f => + { + return f * 9; + }; + $$ + } + """, """ + using System; + class TestClass + { + Func Y() => f => + { + return f * 9; + }$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedMethodWithExpressionBodiedSimpleLambdaExpression() { - Test(@"using System; -class TestClass -{ - Func Y() => f => f * 9; - $$ -}", @"using System; -class TestClass -{ - Func Y() => f => f * 9$$ -}"); + Test(""" + using System; + class TestClass + { + Func Y() => f => f * 9; + $$ + } + """, """ + using System; + class TestClass + { + Func Y() => f => f * 9$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void ExpressionBodiedMethodWithBlockBodiedAnonymousMethodExpressionInMethodArgs() { - Test(@"using System; -class TestClass -{ - public int Prop => Method1(delegate () - { - return 8; - }); - $$ + Test(""" + using System; + class TestClass + { + public int Prop => Method1(delegate () + { + return 8; + }); + $$ - private int Method1(Func p) => null; -}", @"using System; -class TestClass -{ - public int Prop => Method1(delegate() - { - return 8; - })$$ + private int Method1(Func p) => null; + } + """, """ + using System; + class TestClass + { + public int Prop => Method1(delegate() + { + return 8; + })$$ - private int Method1(Func p) => null; -}"); + private int Method1(Func p) => null; + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void Format_SimpleExpressionBodiedMember() { - Test(@"class T -{ - int M() => 1 + 2; - $$ -}", @"class T -{ - int M() => 1 + 2$$ -}"); + Test(""" + class T + { + int M() => 1 + 2; + $$ + } + """, """ + class T + { + int M() => 1 + 2$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void Format_ExpressionBodiedMemberWithSingleLineBlock() { - Test(@"using System; -class TestClass -{ - Func Y() => delegate (int x) { return 9; }; - $$ -}", @"using System; -class TestClass -{ - Func Y () => delegate(int x) { return 9 ; }$$ -}"); + Test(""" + using System; + class TestClass + { + Func Y() => delegate (int x) { return 9; }; + $$ + } + """, """ + using System; + class TestClass + { + Func Y () => delegate(int x) { return 9 ; }$$ + } + """); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/3944")] public void Format_ExpressionBodiedMemberWithMultiLineBlock() { - Test(@"using System; -class TestClass -{ - Func Y() => delegate (int x) - { - return 9; - }; - $$ -}", @"using System; -class TestClass -{ - Func Y() => delegate(int x) - { - return 9; - }$$ -}"); + Test(""" + using System; + class TestClass + { + Func Y() => delegate (int x) + { + return 9; + }; + $$ + } + """, """ + using System; + class TestClass + { + Func Y() => delegate(int x) + { + return 9; + }$$ + } + """); } [WpfFact] public void Format_Statement() { - Test(@"class C -{ - void Method() - { - int i = 1; - $$ - } -}", @"class C -{ - void Method() - { - int i = 1 $$ - } -}"); + Test(""" + class C + { + void Method() + { + int i = 1; + $$ + } + } + """, """ + class C + { + void Method() + { + int i = 1 $$ + } + } + """); } [WpfFact] public void Format_Using() { - Test(@"using System.Linq; -$$", @" using System . Linq $$"); + Test(""" + using System.Linq; + $$ + """, @" using System . Linq $$"); } [WpfFact] public void Format_Using2() { - Test(@"using - System.Linq; -$$", @" using - System . Linq $$"); + Test(""" + using + System.Linq; + $$ + """, """ + using + System . Linq $$ + """); } [WpfFact] public void Format_Field() { - Test(@"class C -{ - int i = 1; - $$ -}", @"class C -{ - int i = 1 $$ -}"); + Test(""" + class C + { + int i = 1; + $$ + } + """, """ + class C + { + int i = 1 $$ + } + """); } [WpfFact] public void Statement_Trivia() { - Test(@"class C -{ - void goo() - { - goo(); //comment - $$ - } -}", @"class C -{ - void goo() - { - goo()$$ //comment - } -}"); + Test(""" + class C + { + void goo() + { + goo(); //comment + $$ + } + } + """, """ + class C + { + void goo() + { + goo()$$ //comment + } + } + """); } [WpfFact] public void TrailingText_Negative() { - Test(@"class C -{ - event System.EventHandler e = null int i = 2; - $$ -}", @"class C -{ - event System.EventHandler e = null$$ int i = 2; -}"); + Test(""" + class C + { + event System.EventHandler e = null int i = 2; + $$ + } + """, """ + class C + { + event System.EventHandler e = null$$ int i = 2; + } + """); } [WpfFact] public void CompletionSetUp() { - Test(@"class Program -{ - object goo(object o) - { - return goo(); - $$ - } -}", @"class Program -{ - object goo(object o) - { - return goo($$) - } -}", completionActive: true); + Test(""" + class Program + { + object goo(object o) + { + return goo(); + $$ + } + } + """, """ + class Program + { + object goo(object o) + { + return goo($$) + } + } + """, completionActive: true); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530352")] - public void EmbededStatement3() - { - Test(@"class Program -{ - void Method() + public void EmbeddedStatement3() { - foreach (var x in y) - { - $$ - } + Test(""" + class Program + { + void Method() + { + foreach (var x in y) + { + $$ + } + } + } + """, """ + class Program + { + void Method() + { + foreach (var x in y$$) + } + } + """); } -}", @"class Program -{ - void Method() + + [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/66102")] + public void EmbeddedStatement4() { - foreach (var x in y$$) - } -}"); + Test(""" + class Program + { + void Method() + { + foreach (var (x, y) in z) + { + $$ + } + } + } + """, """ + class Program + { + void Method() + { + foreach (var (x, y) in z$$) + } + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530716")] public void DoNotAssertOnMultilineToken() { - Test(@"interface I -{ - void M(string s = @"""""" -$$ -}", @"interface I -{ - void M(string s = @""""""$$ -}"); + Test("""" + interface I + { + void M(string s = @""" + $$ + } + """", """" + interface I + { + void M(string s = @"""$$ + } + """"); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530718")] public void AutomaticLineFormat() { - Test(@"class C -{ - public string P { set; get; } - $$ -}", @"class C -{ - public string P {set;get;$$} -}"); + Test(""" + class C + { + public string P { set; get; } + $$ + } + """, """ + class C + { + public string P {set;get;$$} + } + """); } [WpfFact] public void NotAfterExisitingSemicolon() { - Test(@"class TestClass -{ - private int i; - $$ -}", @"class TestClass -{ - private int i;$$ -}"); + Test(""" + class TestClass + { + private int i; + $$ + } + """, """ + class TestClass + { + private int i;$$ + } + """); } [WpfFact] public void NotAfterCloseBraceInMethod() { - Test(@"class TestClass -{ - void Test() { } - $$ -}", @"class TestClass -{ - void Test() { }$$ -}"); + Test(""" + class TestClass + { + void Test() { } + $$ + } + """, """ + class TestClass + { + void Test() { }$$ + } + """); } [WpfFact] public void NotAfterCloseBraceInStatement() { - Test(@"class TestClass -{ - void Test() - { - if (true) { } - $$ - } -}", @"class TestClass -{ - void Test() - { - if (true) { }$$ - } -}"); + Test(""" + class TestClass + { + void Test() + { + if (true) { } + $$ + } + } + """, """ + class TestClass + { + void Test() + { + if (true) { }$$ + } + } + """); } [WpfFact] public void NotAfterAutoPropertyAccessor() { - Test(@"class TestClass -{ - public int A { get; set } - $$ -}", @"class TestClass -{ - public int A { get; set$$ } -}"); + Test(""" + class TestClass + { + public int A { get; set } + $$ + } + """, """ + class TestClass + { + public int A { get; set$$ } + } + """); } [WpfFact] public void NotAfterAutoPropertyDeclaration() { - Test(@"class TestClass -{ - public int A { get; set; } - $$ -}", @"class TestClass -{ - public int A { get; set; }$$ -}"); + Test(""" + class TestClass + { + public int A { get; set; } + $$ + } + """, """ + class TestClass + { + public int A { get; set; }$$ + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/150480")] public void DelegatedInEmptyBlock() { - Test(@"class TestClass -{ - void Method() - { - try { $$} - } -}", @"class TestClass -{ - void Method() - { - try { $$} - } -}", assertNextHandlerInvoked: true); + Test(""" + class TestClass + { + void Method() + { + try { $$} + } + } + """, """ + class TestClass + { + void Method() + { + try { $$} + } + } + """, assertNextHandlerInvoked: true); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/150480")] public void DelegatedInEmptyBlock2() { - Test(@"class TestClass -{ - void Method() - { - if (true) { $$} - } -}", @"class TestClass -{ - void Method() - { - if (true) { $$} - } -}", assertNextHandlerInvoked: true); + Test(""" + class TestClass + { + void Method() + { + if (true) { $$} + } + } + """, """ + class TestClass + { + void Method() + { + if (true) { $$} + } + } + """, assertNextHandlerInvoked: true); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/150480")] public void NotDelegatedOutsideEmptyBlock() { - Test(@"class TestClass -{ - void Method() - { - try { } - $$ - } -}", @"class TestClass -{ - void Method() - { - try { }$$ - } -}"); + Test(""" + class TestClass + { + void Method() + { + try { } + $$ + } + } + """, """ + class TestClass + { + void Method() + { + try { }$$ + } + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/150480")] public void NotDelegatedAfterOpenBraceAndMissingCloseBrace() { - Test(@"class TestClass -{ - void Method() - { - try { - $$ - } -}", @"class TestClass -{ - void Method() - { - try {$$ - } -}"); + Test(""" + class TestClass + { + void Method() + { + try { + $$ + } + } + """, """ + class TestClass + { + void Method() + { + try {$$ + } + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/150480")] public void NotDelegatedInNonEmptyBlock() { - Test(@"class TestClass -{ - void Method() - { - try { x } - $$ - } -}", @"class TestClass -{ - void Method() - { - try { x$$ } - } -}"); + Test(""" + class TestClass + { + void Method() + { + try { x } + $$ + } + } + """, """ + class TestClass + { + void Method() + { + try { x$$ } + } + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/150480")] public void NotDelegatedAfterOpenBraceInAnonymousObjectCreationExpression() { - Test(@"class TestClass -{ - void Method() - { - var pet = new { }; - $$ - } -}", @"class TestClass -{ - void Method() - { - var pet = new { $$} - } -}"); + Test(""" + class TestClass + { + void Method() + { + var pet = new { }; + $$ + } + } + """, """ + class TestClass + { + void Method() + { + var pet = new { $$} + } + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/150480")] public void NotDelegatedAfterOpenBraceObjectCreationExpression() { - Test(@"class TestClass -{ - void Method() - { - var pet = new List(); - $$ - } -}", @"class TestClass -{ - void Method() - { - var pet = new List { $$} - } -}"); + Test(""" + class TestClass + { + void Method() + { + var pet = new List(); + $$ + } + } + """, """ + class TestClass + { + void Method() + { + var pet = new List { $$} + } + } + """); } [WpfFact] - public void TestMulitpleNamespace() + public void TestMultipleNamespace() { - Test($@" -namespace Bar2 -{{ - $$ -}} -namespace Bar -{{ -}}", $@" -namespace B$$ar2$$ -namespace Bar -{{ -}}"); + Test($$""" + namespace Bar2 + { + $$ + } + namespace Bar + { + } + """, $$""" + namespace B$$ar2$$ + namespace Bar + { + } + """); } [WpfTheory] @@ -883,12 +1106,14 @@ namespace Bar [InlineData("interface")] public void TestEmptyBaseTypeDeclarationAndNamespace(string typeKeyword) { - Test($@" -public {typeKeyword} Bar -{{ - $$ -}}", $@" -pu$$blic {typeKeyword} $$Bar$$"); + Test($$""" + public {{typeKeyword}} Bar + { + $$ + } + """, $""" + pu$$blic {typeKeyword} $$Bar$$ + """); } [WpfTheory] @@ -899,55 +1124,61 @@ public void TestEmptyBaseTypeDeclarationAndNamespace(string typeKeyword) [InlineData("interface")] public void TestMultipleBaseTypeDeclaration(string typeKeyword) { - Test($@" -public {typeKeyword} Bar2 -{{ - $$ -}} + Test($$""" + public {{typeKeyword}} Bar2 + { + $$ + } -public {typeKeyword} Bar -{{ -}}", $@" -pub$$lic {typeKeyword} B$$ar2$$ -public {typeKeyword} Bar -{{ -}}"); + public {{typeKeyword}} Bar + { + } + """, $$""" + pub$$lic {{typeKeyword}} B$$ar2$$ + public {{typeKeyword}} Bar + { + } + """); } [WpfFact] public void TestNestedTypeDeclaration() { - Test(@" -public class Bar1 -{ - public class Bar2 - { - $$ - } -}", -@" -public class Bar1 -{ - pu$$blic cla$$ss B$$ar2$$ -}"); + Test(""" + public class Bar1 + { + public class Bar2 + { + $$ + } + } + """, + """ + public class Bar1 + { + pu$$blic cla$$ss B$$ar2$$ + } + """); } [WpfFact] public void TestNestedNamespace() { - Test(@" -namespace Bar1 -{ - namespace Bar2 - { - $$ - } -}", -@" -namespace Bar1 -{ - namespa$$ce $$B$$ar2$$ -}"); + Test(""" + namespace Bar1 + { + namespace Bar2 + { + $$ + } + } + """, + """ + namespace Bar1 + { + namespa$$ce $$B$$ar2$$ + } + """); } [WpfTheory] @@ -959,10 +1190,12 @@ namespace Bar1 [InlineData("interface")] public void TestBaseTypeDeclarationAndNamespaceWithOpenBrace(string typeKeyword) { - Test($@" -public {typeKeyword} Bar {{ - $$", $@" -pub$$lic {typeKeyword} B$$ar {{$$"); + Test($$""" + public {{typeKeyword}} Bar { + $$ + """, $$""" + pub$$lic {{typeKeyword}} B$$ar {$$ + """); } [WpfTheory] @@ -974,210 +1207,233 @@ public void TestBaseTypeDeclarationAndNamespaceWithOpenBrace(string typeKeyword) [InlineData("interface")] public void TestValidTypeDeclarationAndNamespace(string typeKeyword) { - Test($@"public {typeKeyword} Bar {{}} -$$", + Test($$""" + public {{typeKeyword}} Bar {} + $$ + """, $@"public {typeKeyword}$$ Ba$$r {{}}$$"); } [WpfFact] public void TestMethod() { - Test(@" -public class Bar -{ - void Main() - { - $$ - } -}", @" -public class Bar -{ - v$$oid Ma$$in($$)$$ -}"); + Test(""" + public class Bar + { + void Main() + { + $$ + } + } + """, """ + public class Bar + { + v$$oid Ma$$in($$)$$ + } + """); } [WpfFact] public void TestConstructor() { - Test(@" -public class Bar -{ - void Bar() - { - $$ - } -}", @" -public class Bar -{ - v$$oid Ba$$r($$)$$ -}"); + Test(""" + public class Bar + { + void Bar() + { + $$ + } + } + """, """ + public class Bar + { + v$$oid Ba$$r($$)$$ + } + """); } [WpfFact] public void TestValidMethodInInterface() { - Test(@" -public interface Bar -{ - void Main(); - $$ -}", @" -public interface Bar -{ - v$$oid Mai$$n($$)$$; -}"); + Test(""" + public interface Bar + { + void Main(); + $$ + } + """, """ + public interface Bar + { + v$$oid Mai$$n($$)$$; + } + """); } [WpfFact] public void TestMissingSemicolonMethodInInterface() { - Test(@" -public interface Bar -{ - void Main() - $$ -}", @" -public interface Bar -{ - v$$oid Mai$$n($$)$$ -}"); + Test(""" + public interface Bar + { + void Main() + $$ + } + """, """ + public interface Bar + { + v$$oid Mai$$n($$)$$ + } + """); } [WpfFact] public void TestValidLocalFunction() { - Test(@" -public class Bar -{ - void Main() - { - void Local() - $$ - { - } - } -}", @" -public class Bar -{ - void Main() - { - v$$oid Loc$$al($$)$$ - { - } - } -}"); + Test(""" + public class Bar + { + void Main() + { + void Local() + $$ + { + } + } + } + """, """ + public class Bar + { + void Main() + { + v$$oid Loc$$al($$)$$ + { + } + } + } + """); } [WpfFact] public void TestLocalFunction() { - Test(@" -public class Bar -{ - void Main() - { - void Local() - { - $$ - } - } -}", @" -public class Bar -{ - void Main() - { - v$$oid Loca$$l($$)$$ - } -}"); + Test(""" + public class Bar + { + void Main() + { + void Local() + { + $$ + } + } + } + """, """ + public class Bar + { + void Main() + { + v$$oid Loca$$l($$)$$ + } + } + """); } [WpfFact] public void TestIndexerAsLastElementInClass() { - Test(@" -public class Bar -{ - public int this[int i] - { - $$ - } -}", @" -public class Bar -{ - p$$ublic in$$t thi$$s[in$$t i]$$ -}"); + Test(""" + public class Bar + { + public int this[int i] + { + $$ + } + } + """, """ + public class Bar + { + p$$ublic in$$t thi$$s[in$$t i]$$ + } + """); } [WpfFact] public void TestIndexerNotAsLastElementInClass() { - Test(@" -public class Bar -{ - public int this[int i] - { - $$ - } - void Main() {} -}", @" -public class Bar -{ - p$$ublic in$$t thi$$s[in$$t i]$$ - void Main() {} -}"); + Test(""" + public class Bar + { + public int this[int i] + { + $$ + } + void Main() {} + } + """, """ + public class Bar + { + p$$ublic in$$t thi$$s[in$$t i]$$ + void Main() {} + } + """); } [WpfFact] public void TestValidIndexer() { - Test(@" -public class Bar -{ - public int this[int i] - $$ - { - } -}", @" -public class Bar -{ - p$$ublic i$$nt thi$$s[in$$t i]$$ - { - } -}"); + Test(""" + public class Bar + { + public int this[int i] + $$ + { + } + } + """, """ + public class Bar + { + p$$ublic i$$nt thi$$s[in$$t i]$$ + { + } + } + """); } [WpfFact] public void TestGetAccessorOfProperty() { - var initialMarkup = @" -public class Bar -{ - public int P - { - ge$$t$$ - } -}"; + var initialMarkup = """ + public class Bar + { + public int P + { + ge$$t$$ + } + } + """; - var firstResult = @" -public class Bar -{ - public int P - { - get - { - $$ - } - } -}"; - var secondResult = @" -public class Bar -{ - public int P - { - get; - $$ - } -}"; + var firstResult = """ + public class Bar + { + public int P + { + get + { + $$ + } + } + } + """; + var secondResult = """ + public class Bar + { + public int P + { + get; + $$ + } + } + """; Test(firstResult, initialMarkup); Test(secondResult, firstResult); } @@ -1185,34 +1441,37 @@ public int P [WpfFact] public void TestSetAccessorOfProperty() { - var initialMarkup = @" -public class Bar -{ - public int P - { - set$$ - } -}"; - var firstResult = @" -public class Bar -{ - public int P - { - set - { - $$ - } - } -}"; - var secondResult = @" -public class Bar -{ - public int P - { - set; - $$ - } -}"; + var initialMarkup = """ + public class Bar + { + public int P + { + set$$ + } + } + """; + var firstResult = """ + public class Bar + { + public int P + { + set + { + $$ + } + } + } + """; + var secondResult = """ + public class Bar + { + public int P + { + set; + $$ + } + } + """; Test(firstResult, initialMarkup); Test(secondResult, firstResult); } @@ -1220,313 +1479,336 @@ public int P [WpfFact] public void TestGetAccessorOfIndexer() { - Test(@" -public class Bar -{ - public int this[int i] - { - get - { - $$ - } - } -}", @" -public class Bar -{ - public int this[int i] - { - ge$$t$$ - } -}"); + Test(""" + public class Bar + { + public int this[int i] + { + get + { + $$ + } + } + } + """, """ + public class Bar + { + public int this[int i] + { + ge$$t$$ + } + } + """); } [WpfFact] public void TestValidGetAccessorOfIndexer() { - Test(@" -public class Bar -{ - public int this[int i] - { - get - { + Test(""" + public class Bar + { + public int this[int i] + { + get + { - $$ - } - } -}", @" -public class Bar -{ - public int this[int i] - { - get - { - $$ - } - } -}"); + $$ + } + } + } + """, """ + public class Bar + { + public int this[int i] + { + get + { + $$ + } + } + } + """); } [WpfFact] public void TestNonEmptyGetAccessor() { - Test(@" -public Class Bar -{ - public int P - { - get - { - if (true) - $$ + Test(""" + public Class Bar { - return 1; + public int P + { + get + { + if (true) + $$ + { + return 1; + } + } + } } - } - } -}", - @" -public Class Bar -{ - public int P - { - get - { - i$$f ($$true$$)$$ + """, + """ + public Class Bar { - return 1; + public int P + { + get + { + i$$f ($$true$$)$$ + { + return 1; + } + } + } } - } - } -}"); + """); } [WpfFact] public void TestNonEmptySetAccessor() { - Test(@" -public Class Bar -{ - public int P - { - get; - set - { - if (true) - $$ + Test(""" + public Class Bar { + public int P + { + get; + set + { + if (true) + $$ + { + } + } + } } - } - } -}", - @" -public Class Bar -{ - public int P - { - get; - set - { - i$$f (t$$rue)$$ + """, + """ + public Class Bar { + public int P + { + get; + set + { + i$$f (t$$rue)$$ + { + } + } + } } - } - } -}"); + """); } [WpfFact] public void TestSetAccessorOfIndexer() { - Test(@" -public class Bar -{ - public int this[int i] - { - get; - set - { - $$ - } - } -}", @" -public class Bar -{ - public int this[int i] - { - get; - se$$t$$ - } -}"); + Test(""" + public class Bar + { + public int this[int i] + { + get; + set + { + $$ + } + } + } + """, """ + public class Bar + { + public int this[int i] + { + get; + se$$t$$ + } + } + """); } [WpfFact] public void TestValidSetAccessorOfIndexer() { - Test(@" -public class Bar -{ - public int this[int i] - { - get; - set - { + Test(""" + public class Bar + { + public int this[int i] + { + get; + set + { - $$ - } - } -}", @" -public class Bar -{ - public int this[int i] - { - get; - set - { - $$ - } - } -}"); + $$ + } + } + } + """, """ + public class Bar + { + public int this[int i] + { + get; + set + { + $$ + } + } + } + """); } [WpfFact] public void TestAddAccessorInEventDeclaration() { - Test(@" -using System; -public class Bar -{ - public event EventHandler e - { - add - { - $$ - } - remove - } -}", @" -using System; -public class Bar -{ - public event EventHandler e - { - ad$$d$$ - remove - } -}"); + Test(""" + using System; + public class Bar + { + public event EventHandler e + { + add + { + $$ + } + remove + } + } + """, """ + using System; + public class Bar + { + public event EventHandler e + { + ad$$d$$ + remove + } + } + """); } [WpfFact] public void TestValidAddAccessorInEventDeclaration() { - Test(@" -using System; -public class Bar -{ - public event EventHandler e - { - add - { + Test(""" + using System; + public class Bar + { + public event EventHandler e + { + add + { - $$ - } - remove { } - } -}", @" -using System; -public class Bar -{ - public event EventHandler e - { - add - { - $$ - } - remove { } - } -}"); + $$ + } + remove { } + } + } + """, """ + using System; + public class Bar + { + public event EventHandler e + { + add + { + $$ + } + remove { } + } + } + """); } [WpfFact] public void TestRemoveAccessor() { - Test(@" -using System; -public class Bar -{ - public event EventHandler e - { - add - remove - { - $$ - } - } -}", @" -using System; -public class Bar -{ - public event EventHandler e - { - add - remo$$ve$$ - } -}"); + Test(""" + using System; + public class Bar + { + public event EventHandler e + { + add + remove + { + $$ + } + } + } + """, """ + using System; + public class Bar + { + public event EventHandler e + { + add + remo$$ve$$ + } + } + """); } [WpfFact] public void TestValidRemoveAccessor() { - Test(@" -using System; -public class Bar -{ - public event EventHandler e - { - add { } - remove - { + Test(""" + using System; + public class Bar + { + public event EventHandler e + { + add { } + remove + { - $$ - } - } -}", @" -using System; -public class Bar -{ - public event EventHandler e - { - add { } - remove - { - $$ - } - } -}"); + $$ + } + } + } + """, """ + using System; + public class Bar + { + public event EventHandler e + { + add { } + remove + { + $$ + } + } + } + """); } [WpfFact] public void TestField() { - var initialMarkup = @" -public class Bar -{ - p$$ublic i$$nt i$$ii$$ -}"; - var firstResult = @" -public class Bar -{ - public int iii - { - $$ - } -}"; - var secondResult = @" -public class Bar -{ - public int iii; - $$ -}"; + var initialMarkup = """ + public class Bar + { + p$$ublic i$$nt i$$ii$$ + } + """; + var firstResult = """ + public class Bar + { + public int iii + { + $$ + } + } + """; + var secondResult = """ + public class Bar + { + public int iii; + $$ + } + """; Test(firstResult, initialMarkup); Test(secondResult, firstResult); @@ -1535,96 +1817,107 @@ public class Bar [WpfFact] public void TestReadonlyField() { - Test(@" -public class Bar -{ - public readonly int iii; - $$ -}", @" -public class Bar -{ - p$$ublic reado$$nly i$$nt i$$ii$$ -}"); + Test(""" + public class Bar + { + public readonly int iii; + $$ + } + """, """ + public class Bar + { + p$$ublic reado$$nly i$$nt i$$ii$$ + } + """); } [WpfFact] public void TestNonEmptyProperty() { - Test(@" -public class Bar -{ - public int Foo - { - get { } - $$ - } -}", @" -public class Bar -{ - public int Foo - { - $$get$$ { }$$ - } -}"); + Test(""" + public class Bar + { + public int Foo + { + get { } + $$ + } + } + """, """ + public class Bar + { + public int Foo + { + $$get$$ { }$$ + } + } + """); } [WpfFact] - public void TestMulitpleFields() + public void TestMultipleFields() { - Test(@" -public class Bar -{ - public int apple, banana; - $$ -}", @" -public class Bar -{ - p$$ublic i$$nt ap$$ple$$, ba$$nana;$$ -}"); + Test(""" + public class Bar + { + public int apple, banana; + $$ + } + """, """ + public class Bar + { + p$$ublic i$$nt ap$$ple$$, ba$$nana;$$ + } + """); } [WpfFact] public void TestMultipleEvents() { - Test(@" -using System; -public class Bar -{ - public event EventHandler apple, banana; - $$ -}", @" -using System; -public class Bar -{ - p$$ublic event EventHandler ap$$ple$$, ba$$nana$$;$$ -}"); + Test(""" + using System; + public class Bar + { + public event EventHandler apple, banana; + $$ + } + """, """ + using System; + public class Bar + { + p$$ublic event EventHandler ap$$ple$$, ba$$nana$$;$$ + } + """); } [WpfFact] public void TestEvent() { - var initialMarkup = @" -using System; -public class Bar -{ - pu$$blic e$$vent EventHand$$ler c$$c$$ -}"; - var firstResult = @" -using System; -public class Bar -{ - public event EventHandler cc - { - $$ - } -}"; - var secondResult = @" -using System; -public class Bar -{ - public event EventHandler cc; - $$ -}"; + var initialMarkup = """ + using System; + public class Bar + { + pu$$blic e$$vent EventHand$$ler c$$c$$ + } + """; + var firstResult = """ + using System; + public class Bar + { + public event EventHandler cc + { + $$ + } + } + """; + var secondResult = """ + using System; + public class Bar + { + public event EventHandler cc; + $$ + } + """; Test(firstResult, initialMarkup); Test(secondResult, firstResult); } @@ -1632,74 +1925,79 @@ public class Bar [WpfFact] public void TestNonEmptyEvent() { - Test(@" -using System; -public class Bar -{ - public event EventHandler Foo - { - add { } - $$ - } -}", @" -using System; -public class Bar -{ - public event EventHandler Foo - { - $$add$$ {$$ }$$ - } -}"); + Test(""" + using System; + public class Bar + { + public event EventHandler Foo + { + add { } + $$ + } + } + """, """ + using System; + public class Bar + { + public event EventHandler Foo + { + $$add$$ {$$ }$$ + } + } + """); } [WpfFact] public void TestObjectCreationExpressionWithParenthesis() { - var initialMarkup = @" -public class Bar -{ - public void M() - { - var f = n$$ew F$$oo($$)$$ - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var initialMarkup = """ + public class Bar + { + public void M() + { + var f = n$$ew F$$oo($$)$$ + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var firstResult = @" -public class Bar -{ - public void M() - { - var f = new Foo() - { - $$ - }; - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var firstResult = """ + public class Bar + { + public void M() + { + var f = new Foo() + { + $$ + }; + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var secondResult = @" -public class Bar -{ - public void M() - { - var f = new Foo(); - $$ - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var secondResult = """ + public class Bar + { + public void M() + { + var f = new Foo(); + $$ + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; Test(firstResult, initialMarkup); Test(secondResult, firstResult); @@ -1708,51 +2006,54 @@ public class Foo [WpfFact] public void TestObjectCreationExpressionWithNoParenthesis() { - var initialMarkUp = @" -public class Bar -{ - public void M() - { - var f = n$$ew F$$oo$$ - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var initialMarkUp = """ + public class Bar + { + public void M() + { + var f = n$$ew F$$oo$$ + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var firstResult = @" -public class Bar -{ - public void M() - { - var f = new Foo() - { - $$ - }; - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var firstResult = """ + public class Bar + { + public void M() + { + var f = new Foo() + { + $$ + }; + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var secondResult = @" -public class Bar -{ - public void M() - { - var f = new Foo(); - $$ - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var secondResult = """ + public class Bar + { + public void M() + { + var f = new Foo(); + $$ + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; Test(firstResult, initialMarkUp); Test(secondResult, firstResult); @@ -1761,51 +2062,54 @@ public class Foo [WpfFact] public void TestObjectCreationExpressionWithCorrectSemicolon() { - var initialMarkUp = @" -public class Bar -{ - public void M() - { - var f = n$$ew F$$oo$$; - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var initialMarkUp = """ + public class Bar + { + public void M() + { + var f = n$$ew F$$oo$$; + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var firstResult = @" -public class Bar -{ - public void M() - { - var f = new Foo() - { - $$ - }; - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var firstResult = """ + public class Bar + { + public void M() + { + var f = new Foo() + { + $$ + }; + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var secondResult = @" -public class Bar -{ - public void M() - { - var f = new Foo(); - $$ - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var secondResult = """ + public class Bar + { + public void M() + { + var f = new Foo(); + $$ + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; Test(firstResult, initialMarkUp); Test(secondResult, firstResult); @@ -1814,63 +2118,66 @@ public class Foo [WpfFact] public void TestObjectCreationExpressionUsedAsExpression() { - var initialMarkUp = @" -public class Bar -{ - public void M() - { - N(ne$$w Fo$$o$$); - } - - private void N(Foo f) - { - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var initialMarkUp = """ + public class Bar + { + public void M() + { + N(ne$$w Fo$$o$$); + } - var firstResult = @" -public class Bar -{ - public void M() - { - N(new Foo() - { - $$ - }); - } + private void N(Foo f) + { + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - private void N(Foo f) - { - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var firstResult = """ + public class Bar + { + public void M() + { + N(new Foo() + { + $$ + }); + } - var secondResult = @" -public class Bar -{ - public void M() - { - N(new Foo()); - $$ - } + private void N(Foo f) + { + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - private void N(Foo f) - { - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var secondResult = """ + public class Bar + { + public void M() + { + N(new Foo()); + $$ + } + + private void N(Foo f) + { + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; Test(firstResult, initialMarkUp); Test(secondResult, firstResult); @@ -1879,51 +2186,54 @@ public class Foo [WpfFact] public void TestObjectCreationExpressionInUsingStatement() { - var initialMarkup = @" -public class Bar -{ - public void M() - { - using(var a = n$$ew F$$oo($$)$$) - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var initialMarkup = """ + public class Bar + { + public void M() + { + using(var a = n$$ew F$$oo($$)$$) + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var firstResult = @" -public class Bar -{ - public void M() - { - using(var a = new Foo() - { - $$ - }) - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var firstResult = """ + public class Bar + { + public void M() + { + using(var a = new Foo() + { + $$ + }) + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; - var secondResult = @" -public class Bar -{ - public void M() - { - using(var a = new Foo()) - $$ - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"; + var secondResult = """ + public class Bar + { + public void M() + { + using(var a = new Foo()) + $$ + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """; Test(firstResult, initialMarkup); Test(secondResult, firstResult); @@ -1933,33 +2243,35 @@ public class Foo public void TestObjectCreationExpressionWithNonEmptyInitializer() { Test( - @" -public class Bar -{ - public void M() - { - var a = new Foo() { HH = 1, PP = 2 }; - $$ - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}", - @" -public class Bar -{ - public void M() - { - var a = n$$ew Fo$$o($$) {$$ HH = 1$$, PP = 2 $$}; - } -} -public class Foo -{ - public int HH { get; set; } - public int PP { get; set; } -}"); + """ + public class Bar + { + public void M() + { + var a = new Foo() { HH = 1, PP = 2 }; + $$ + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """, + """ + public class Bar + { + public void M() + { + var a = n$$ew Fo$$o($$) {$$ HH = 1$$, PP = 2 $$}; + } + } + public class Foo + { + public int HH { get; set; } + public int PP { get; set; } + } + """); } @@ -2074,779 +2386,833 @@ public void M() [WpfFact] public void TestIfStatementWithInnerStatement() { - Test(@" -public class Bar -{ - public void Main(bool x) - { - if (x) - { - $$ - } - var z = 1; - } -}", @" -public class Bar -{ - public void Main(bool x) - { - i$$f$$ ($$x)$$ - var z = 1; - } -}"); + Test(""" + public class Bar + { + public void Main(bool x) + { + if (x) + { + $$ + } + var z = 1; + } + } + """, """ + public class Bar + { + public void Main(bool x) + { + i$$f$$ ($$x)$$ + var z = 1; + } + } + """); } [WpfFact] public void TestIfStatementWithFollowingElseClause() { - Test(@" -public class Bar -{ - public void Main(bool x) - { - if (x) - { - $$ - var z = 1; - } - else if (!x) - } -}", @" -public class Bar -{ - public void Main(bool x) - { - i$$f$$ ($$x)$$ - var z = 1; - else if (!x) - } -}"); - } - - [WpfFact] - public void TestIfStatementWithoutStatement() - { - Test(@" -public class Bar -{ - public void Main(bool x) - { - if (x) - { - $$ - } - } -}", @" -public class Bar -{ - public void Main(bool x) - { - i$$f$$ ($$x)$$ - } -}"); - } - - [WpfFact] - public void TestNestIfStatementWithInnerStatement() - { - Test(@" -public class Bar -{ - public void Main(int x) - { - if (x == 1) - if (x == 2) - if (x == 3) - if (x == 4) + Test(""" + public class Bar + { + public void Main(bool x) + { + if (x) { $$ - var a = 1000; + var z = 1; } - } -}", @" -public class Bar -{ - public void Main(int x) - { - if (x == 1) - if (x == 2) - if (x == 3) - i$$f ($$x =$$= 4)$$ - var a = 1000; - } -}"); + else if (!x) + } + } + """, """ + public class Bar + { + public void Main(bool x) + { + i$$f$$ ($$x)$$ + var z = 1; + else if (!x) + } + } + """); } [WpfFact] - public void TestNestIfStatementWithoutInnerStatement() - { - Test(@" -public class Bar -{ - public void Main(int x) + public void TestIfStatementWithoutStatement() { - if (x == 1) - if (x == 2) - if (x == 3) - if (x == 4) + Test(""" + public class Bar + { + public void Main(bool x) + { + if (x) { $$ } - } -}", @" -public class Bar -{ - public void Main(int x) - { - if (x == 1) - if (x == 2) - if (x == 3) - i$$f ($$x =$$= 4)$$ - } -}"); + } + } + """, """ + public class Bar + { + public void Main(bool x) + { + i$$f$$ ($$x)$$ + } + } + """); } [WpfFact] - public void TestNestedElseIfStatementWithInnerStatement() - { - Test(@" -public class Bar -{ - public void Fo(int i) + public void TestNestIfStatementWithInnerStatement() { - if (i == 1) - { - } - else if (i == 2) - if (i == 3) + Test(""" + public class Bar { - $$ - var i = 10; + public void Main(int x) + { + if (x == 1) + if (x == 2) + if (x == 3) + if (x == 4) + { + $$ + var a = 1000; + } + } } - else + """, """ + public class Bar { + public void Main(int x) + { + if (x == 1) + if (x == 2) + if (x == 3) + i$$f ($$x =$$= 4)$$ + var a = 1000; + } } + """); } -}", @" -public class Bar -{ - public void Fo(int i) + + [WpfFact] + public void TestNestIfStatementWithoutInnerStatement() { - if (i == 1) - { - } - else if (i == 2) - i$$f (i$$ == 3)$$ - var i = 10; - else + Test(""" + public class Bar { + public void Main(int x) + { + if (x == 1) + if (x == 2) + if (x == 3) + if (x == 4) + { + $$ + } + } } - } -}"); + """, """ + public class Bar + { + public void Main(int x) + { + if (x == 1) + if (x == 2) + if (x == 3) + i$$f ($$x =$$= 4)$$ + } + } + """); } [WpfFact] - public void TestNestIfElseStatementWithBlockWithInnerStatement() - { - Test(@" -public class Bar -{ - public void Main(int x) + public void TestNestedElseIfStatementWithInnerStatement() { - if (x == 1) - if (x == 2) - if (x == 3) + Test(""" + public class Bar + { + public void Fo(int i) { - if (x == 4) + if (i == 1) { - $$ } - var i = 10; + else if (i == 2) + if (i == 3) + { + $$ + var i = 10; + } + else + { + } } - else + } + """, """ + public class Bar + { + public void Fo(int i) { + if (i == 1) + { + } + else if (i == 2) + i$$f (i$$ == 3)$$ + var i = 10; + else + { + } } + } + """); } -}", @" -public class Bar -{ - public void Main(int x) + + [WpfFact] + public void TestNestIfElseStatementWithBlockWithInnerStatement() { - if (x == 1) - if (x == 2) - if (x == 3) + Test(""" + public class Bar + { + public void Main(int x) { - i$$f ($$x =$$= 4)$$ - var i = 10; + if (x == 1) + if (x == 2) + if (x == 3) + { + if (x == 4) + { + $$ + } + var i = 10; + } + else + { + } } - else + } + """, """ + public class Bar + { + public void Main(int x) { + if (x == 1) + if (x == 2) + if (x == 3) + { + i$$f ($$x =$$= 4)$$ + var i = 10; + } + else + { + } } - } -}"); + } + """); } [WpfFact] public void TestEmptyDoStatement() { - Test(@" -public class Bar -{ - public void Main() - { - do - { - $$ - } - } -}", @" -public class Bar -{ - public void Main() - { - d$$o$$ - } -}"); + Test(""" + public class Bar + { + public void Main() + { + do + { + $$ + } + } + } + """, """ + public class Bar + { + public void Main() + { + d$$o$$ + } + } + """); } [WpfFact] public void TestDoStatementWithInnerStatement() { - Test(@" -public class Bar -{ - public void Main() - { - do - { - $$ - } - var c = 10; - } -}", @" -public class Bar -{ - public void Main() - { - d$$o$$ - var c = 10; - } -}"); + Test(""" + public class Bar + { + public void Main() + { + do + { + $$ + } + var c = 10; + } + } + """, """ + public class Bar + { + public void Main() + { + d$$o$$ + var c = 10; + } + } + """); } [WpfFact] public void TestDoStatementWithWhileClause() { - Test(@" -public class Bar -{ - public void Main() - { - do - { - $$ - var c = 10; - } - while (true); - } -}", @" -public class Bar -{ - public void Main() - { - d$$o$$ - var c = 10; - while (true); - } -}"); + Test(""" + public class Bar + { + public void Main() + { + do + { + $$ + var c = 10; + } + while (true); + } + } + """, """ + public class Bar + { + public void Main() + { + d$$o$$ + var c = 10; + while (true); + } + } + """); } [WpfFact] public void TestSingleElseStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - if (true) - { - } - else - { - $$ - } - } -}", @" -public class Bar -{ - public void Fo() - { - if (true) - { - } - e$$lse$$ - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + if (true) + { + } + else + { + $$ + } + } + } + """, """ + public class Bar + { + public void Fo() + { + if (true) + { + } + e$$lse$$ + } + } + """); } [WpfFact] public void TestElseStatementWithInnerStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - if (true) - { - } - else - { - $$ - } - var c = 10; - } -}", @" -public class Bar -{ - public void Fo() - { - if (true) - { - } - e$$lse$$ - var c = 10; - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + if (true) + { + } + else + { + $$ + } + var c = 10; + } + } + """, """ + public class Bar + { + public void Fo() + { + if (true) + { + } + e$$lse$$ + var c = 10; + } + } + """); } [WpfFact] public void TestElseIfStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - if (true) - { - } - else if (false) - { - $$ - } - } -}", @" -public class Bar -{ - public void Fo() - { - if (true) - { - } - e$$lse i$$f ($$false)$$ - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + if (true) + { + } + else if (false) + { + $$ + } + } + } + """, """ + public class Bar + { + public void Fo() + { + if (true) + { + } + e$$lse i$$f ($$false)$$ + } + } + """); } [WpfFact] public void TestElseIfInTheMiddleWithInnerStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - if (true) - { - } - else if (false) - { - $$ - var i = 10; - } - else - { - } - } -}", @" -public class Bar -{ - public void Fo() - { - if (true) - { - } - e$$lse i$$f ($$false)$$ - var i = 10; - else - { - } - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + if (true) + { + } + else if (false) + { + $$ + var i = 10; + } + else + { + } + } + } + """, """ + public class Bar + { + public void Fo() + { + if (true) + { + } + e$$lse i$$f ($$false)$$ + var i = 10; + else + { + } + } + } + """); } [WpfFact] public void TestElseClauseInNestedIfStatement() { - Test(@" -public class Bar -{ - public void Fo(int i) - { - if (i == 1) - { - if (i == 2) - var i = 10; - else + Test(""" + public class Bar { - $$ + public void Fo(int i) + { + if (i == 1) + { + if (i == 2) + var i = 10; + else + { + $$ + } + var c = 100; + } + } } - var c = 100; - } - } -}", @" -public class Bar -{ - public void Fo(int i) - { - if (i == 1) - { - if (i == 2) - var i = 10; - el$$se - var c = 100; - } - } -}"); + """, """ + public class Bar + { + public void Fo(int i) + { + if (i == 1) + { + if (i == 2) + var i = 10; + el$$se + var c = 100; + } + } + } + """); } [WpfFact] public void TestForStatementWithoutStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - for (int i; i < 10; i++) - { - $$ - } - } -}", @" -public class Bar -{ - public void Fo() - { - f$$or (i$$nt i; i < 10;$$ i++)$$ - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + for (int i; i < 10; i++) + { + $$ + } + } + } + """, """ + public class Bar + { + public void Fo() + { + f$$or (i$$nt i; i < 10;$$ i++)$$ + } + } + """); } [WpfFact] public void TestForStatementWithInnerStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - for (int i; i < 10; i++) - { - $$ - } - var c = 10; - } -}", @" -public class Bar -{ - public void Fo() - { - f$$or (i$$nt i; i < 10;$$ i++)$$ - var c = 10; - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + for (int i; i < 10; i++) + { + $$ + } + var c = 10; + } + } + """, """ + public class Bar + { + public void Fo() + { + f$$or (i$$nt i; i < 10;$$ i++)$$ + var c = 10; + } + } + """); } [WpfFact] public void TestForEachStatementWithoutInnerStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - foreach (var x in """") - { - $$ - } - var c = 10; - } -}", @" -public class Bar -{ - public void Fo() - { - forea$$ch (var x $$in """")$$ - var c = 10; - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + foreach (var x in "") + { + $$ + } + var c = 10; + } + } + """, """ + public class Bar + { + public void Fo() + { + forea$$ch (var x $$in "")$$ + var c = 10; + } + } + """); } [WpfFact] public void TestLockStatementWithoutInnerStatement() { - Test(@" -public class Bar -{ - object o = new object(); - public void Fo() - { - lock (o) - { - $$ - } - } -}", @" -public class Bar -{ - object o = new object(); - public void Fo() - { - l$$ock$$(o)$$ - } -}"); + Test(""" + public class Bar + { + object o = new object(); + public void Fo() + { + lock (o) + { + $$ + } + } + } + """, """ + public class Bar + { + object o = new object(); + public void Fo() + { + l$$ock$$(o)$$ + } + } + """); } [WpfFact] public void TestLockStatementWithInnerStatement() { - Test(@" -public class Bar -{ - object o = new object(); - public void Fo() - { - lock (o) - { - $$ - } - var i = 10; - } -}", @" -public class Bar -{ - object o = new object(); - public void Fo() - { - l$$ock$$(o)$$ - var i = 10; - } -}"); + Test(""" + public class Bar + { + object o = new object(); + public void Fo() + { + lock (o) + { + $$ + } + var i = 10; + } + } + """, """ + public class Bar + { + object o = new object(); + public void Fo() + { + l$$ock$$(o)$$ + var i = 10; + } + } + """); } [WpfFact] public void TestUsingStatementWithoutInnerStatement() { - Test(@" -using System; -public class Bar -{ - public void Fo() - { - using (var d = new D()) - { - $$ - } - } -} -public class D : IDisposable -{ - public void Dispose() - {} -}", @" -using System; -public class Bar -{ - public void Fo() - { - usi$$ng (va$$r d = new D())$$ - } -} -public class D : IDisposable -{ - public void Dispose() - {} -}"); + Test(""" + using System; + public class Bar + { + public void Fo() + { + using (var d = new D()) + { + $$ + } + } + } + public class D : IDisposable + { + public void Dispose() + {} + } + """, """ + using System; + public class Bar + { + public void Fo() + { + usi$$ng (va$$r d = new D())$$ + } + } + public class D : IDisposable + { + public void Dispose() + {} + } + """); } [WpfFact] public void TestUsingStatementWithInnerStatement() { - Test(@" -using System; -public class Bar -{ - public void Fo() - { - using (var d = new D()) - { - $$ - } - var c = 10; - } -} -public class D : IDisposable -{ - public void Dispose() - {} -}", @" -using System; -public class Bar -{ - public void Fo() - { - usi$$ng (va$$r d = new D())$$ - var c = 10; - } -} -public class D : IDisposable -{ - public void Dispose() - {} -}"); + Test(""" + using System; + public class Bar + { + public void Fo() + { + using (var d = new D()) + { + $$ + } + var c = 10; + } + } + public class D : IDisposable + { + public void Dispose() + {} + } + """, """ + using System; + public class Bar + { + public void Fo() + { + usi$$ng (va$$r d = new D())$$ + var c = 10; + } + } + public class D : IDisposable + { + public void Dispose() + {} + } + """); } [WpfFact] public void TestUsingInLocalDeclarationStatement() { - Test(@" -using System; -public class Bar -{ - public void Fo() - { - using var d = new D(); - $$ - } -} -public class D : IDisposable -{ - public void Dispose() - {} -}", @" -using System; -public class Bar -{ - public void Fo() - { - usi$$ng v$$ar$$ d = new D() - } -} -public class D : IDisposable -{ - public void Dispose() - {} -}"); + Test(""" + using System; + public class Bar + { + public void Fo() + { + using var d = new D(); + $$ + } + } + public class D : IDisposable + { + public void Dispose() + {} + } + """, """ + using System; + public class Bar + { + public void Fo() + { + usi$$ng v$$ar$$ d = new D() + } + } + public class D : IDisposable + { + public void Dispose() + {} + } + """); } [WpfFact] public void TestWhileStatementWithoutInnerStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - while (true) - { - $$ - } - } -}", @" -public class Bar -{ - public void Fo() - { - wh$$ile (tr$$ue)$$ - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + while (true) + { + $$ + } + } + } + """, """ + public class Bar + { + public void Fo() + { + wh$$ile (tr$$ue)$$ + } + } + """); } [WpfFact] public void TestWhileStatementWithInnerStatement() { - Test(@" -public class Bar -{ - public void Fo() - { - while (true) - { - $$ - } - var c = 10; - } -}", @" -public class Bar -{ - public void Fo() - { - wh$$ile (tr$$ue)$$ - var c = 10; - } -}"); + Test(""" + public class Bar + { + public void Fo() + { + while (true) + { + $$ + } + var c = 10; + } + } + """, """ + public class Bar + { + public void Fo() + { + wh$$ile (tr$$ue)$$ + var c = 10; + } + } + """); } [WpfFact] public void TestSwitchExpression1() { - Test(@" -public class Bar -{ - public void Goo(int c) - { - var d = c switch - { - $$ - } - } -}", - @" -public class Bar -{ - public void Goo(int c) - { - var d = c swi$$tch$$ - } -}"); + Test(""" + public class Bar + { + public void Goo(int c) + { + var d = c switch + { + $$ + } + } + } + """, + """ + public class Bar + { + public void Goo(int c) + { + var d = c swi$$tch$$ + } + } + """); } [WpfFact] public void TestSwitchExpression2() { - Test(@" -public class Bar -{ - public void Goo(int c) - { - var d = (c + 1) switch - { - $$ - } - } -}", - @" -public class Bar -{ - public void Goo(int c) - { - var d = (c + 1) swi$$tch$$ - } -}"); + Test(""" + public class Bar + { + public void Goo(int c) + { + var d = (c + 1) switch + { + $$ + } + } + } + """, + """ + public class Bar + { + public void Goo(int c) + { + var d = (c + 1) swi$$tch$$ + } + } + """); } @@ -2856,363 +3222,389 @@ public void TestSwitchStatementWithOnlyOpenParenthesis() // This test is to make sure {} will be added to the switch statement, // but our formatter now can't format the case when the CloseParenthesis token is missing. // If any future formatter improvement can handle this case, this test can be modified safely - Test(@" -public class bar -{ - public void TT() - { - switch ( -{ - $$ - } - } -}", @" -public class bar -{ - public void TT() - { - swi$$tch ($$ - } -}"); + Test(""" + public class bar + { + public void TT() + { + switch ( + { + $$ + } + } + } + """, """ + public class bar + { + public void TT() + { + swi$$tch ($$ + } + } + """); } [WpfFact] public void TestSwitchStatement() { - Test(@" -public class bar -{ - public void TT() - { - int i = 10; - switch (i) - { - $$ - } - } -}", @" -public class bar -{ - public void TT() - { - int i = 10; - switc$$h ($$i)$$ - } -}"); + Test(""" + public class bar + { + public void TT() + { + int i = 10; + switch (i) + { + $$ + } + } + } + """, """ + public class bar + { + public void TT() + { + int i = 10; + switc$$h ($$i)$$ + } + } + """); } [WpfFact] public void TestValidSwitchStatement() { - Test(@" -public class bar -{ - public void TT() - { - int i = 10; - switch (i) - $$ - { - } - } -}", @" -public class bar -{ - public void TT() - { - int i = 10; - switc$$h ($$i)$$ - { - } - } -}"); + Test(""" + public class bar + { + public void TT() + { + int i = 10; + switch (i) + $$ + { + } + } + } + """, """ + public class bar + { + public void TT() + { + int i = 10; + switc$$h ($$i)$$ + { + } + } + } + """); } [WpfFact] public void TestValidTryStatement() { - Test(@" -public class bar -{ - public void TT() - { - try - $$ - { - } - } -}", @" -public class bar -{ - public void TT() - { - tr$$y$$ - { - } - } -}"); + Test(""" + public class bar + { + public void TT() + { + try + $$ + { + } + } + } + """, """ + public class bar + { + public void TT() + { + tr$$y$$ + { + } + } + } + """); } [WpfFact] public void TestTryStatement() { - Test(@" -public class bar -{ - public void TT() - { - try - { - $$ - } - } -}", @" -public class bar -{ - public void TT() - { - tr$$y$$ - } -}"); - } - - [WpfFact] - public void TestValidCatchClause() - { - Test(@" -public class Bar -{ - public void TT() - { - try - { - } - catch (System.Exception) - $$ - { - } - } -}", @" -public class Bar -{ - public void TT() - { - try - { - } - cat$$ch (Syste$$m.Exception)$$ - { - } - } -}"); + Test(""" + public class bar + { + public void TT() + { + try + { + $$ + } + } + } + """, """ + public class bar + { + public void TT() + { + tr$$y$$ + } + } + """); } [WpfFact] - public void TestCatchClauseWithException() - { - Test(@" -public class Bar -{ - public void TT() + public void TestValidCatchClause() { - try - { - } - catch (System.Exception) - { - $$ - } + Test(""" + public class Bar + { + public void TT() + { + try + { + } + catch (System.Exception) + $$ + { + } + } + } + """, """ + public class Bar + { + public void TT() + { + try + { + } + cat$$ch (Syste$$m.Exception)$$ + { + } + } + } + """); } -}", @" -public class Bar -{ - public void TT() + + [WpfFact] + public void TestCatchClauseWithException() { - try - { - } - cat$$ch (Syste$$m.Exception)$$ - } -}"); + Test(""" + public class Bar + { + public void TT() + { + try + { + } + catch (System.Exception) + { + $$ + } + } + } + """, """ + public class Bar + { + public void TT() + { + try + { + } + cat$$ch (Syste$$m.Exception)$$ + } + } + """); } [WpfFact] public void TestSingleCatchClause() { - Test(@" -public class bar -{ - public void TT() - { - try - { - } - catch - { - $$ - } - } -}", @" -public class bar -{ - public void TT() - { - try - { - } - cat$$ch$$ - } -}"); + Test(""" + public class bar + { + public void TT() + { + try + { + } + catch + { + $$ + } + } + } + """, """ + public class bar + { + public void TT() + { + try + { + } + cat$$ch$$ + } + } + """); } [WpfFact] public void TestCatchClauseWithWhenClause() { - Test(@" -public class bar -{ - public void TT() - { - try - { - } - catch (Exception) when (true) - { - $$ - } - } -}", @" -public class bar -{ - public void TT() - { - try - { - } - c$$atch (Ex$$ception) whe$$n (tru$$e)$$ - } -}"); + Test(""" + public class bar + { + public void TT() + { + try + { + } + catch (Exception) when (true) + { + $$ + } + } + } + """, """ + public class bar + { + public void TT() + { + try + { + } + c$$atch (Ex$$ception) whe$$n (tru$$e)$$ + } + } + """); } [WpfFact] - public void TestFinallyCaluse() - { - Test(@" -public class Bar -{ - public void Bar2() - { - try - { - } - catch (System.Exception) - { - } - finally - { - $$ - } - } -}", @" -public class Bar -{ - public void Bar2() + public void TestFinallyClause() { - try - { - } - catch (System.Exception) - { - } - fin$$ally$$ - } -}"); + Test(""" + public class Bar + { + public void Bar2() + { + try + { + } + catch (System.Exception) + { + } + finally + { + $$ + } + } + } + """, """ + public class Bar + { + public void Bar2() + { + try + { + } + catch (System.Exception) + { + } + fin$$ally$$ + } + } + """); } [WpfFact] - public void TestValidFinallyCaluse() - { - Test(@" -public class Bar -{ - public void Bar2() - { - try - { - } - catch (System.Exception) - { - } - finally - $$ - { - } - } -}", @" -public class Bar -{ - public void Bar2() + public void TestValidFinallyClause() { - try - { - } - catch (System.Exception) - { - } - fin$$ally$$ - { - } - } -}"); + Test(""" + public class Bar + { + public void Bar2() + { + try + { + } + catch (System.Exception) + { + } + finally + $$ + { + } + } + } + """, """ + public class Bar + { + public void Bar2() + { + try + { + } + catch (System.Exception) + { + } + fin$$ally$$ + { + } + } + } + """); } [WpfFact] public void TestObjectCreationExpressionWithMissingType() { - Test(@" -public class Bar -{ - public void Bar2() - { - Bar b = new() - { - $$ - }; - } -}", -@" -public class Bar -{ - public void Bar2() - { - Bar b = new$$ - } -}"); + Test(""" + public class Bar + { + public void Bar2() + { + Bar b = new() + { + $$ + }; + } + } + """, + """ + public class Bar + { + public void Bar2() + { + Bar b = new$$ + } + } + """); } [WpfFact] public void TestRemoveInitializerForImplicitObjectCreationExpression() { - Test(@" -public class Bar -{ - public void Bar2() - { - Bar b = new(); - $$ - } -}", -@" -public class Bar -{ - public void Bar2() - { - Bar b = new() - { - $$ - }; - } -}"); + Test(""" + public class Bar + { + public void Bar2() + { + Bar b = new(); + $$ + } + } + """, + """ + public class Bar + { + public void Bar2() + { + Bar b = new() + { + $$ + }; + } + } + """); } [WpfTheory] @@ -3220,25 +3612,27 @@ public void Bar2() [InlineData("unchecked")] public void TestCheckedStatement(string keywordToken) { - Test($@" -public class Bar -{{ - public void Bar2() - {{ - {keywordToken} - {{ - $$ - }} - }} -}}", -$@" -public class Bar -{{ - public void Bar2() - {{ - {keywordToken}$$ - }} -}}"); + Test($$""" + public class Bar + { + public void Bar2() + { + {{keywordToken}} + { + $$ + } + } + } + """, + $$""" + public class Bar + { + public void Bar2() + { + {{keywordToken}}$$ + } + } + """); } [WpfTheory] @@ -3246,79 +3640,85 @@ public void Bar2() [InlineData("unchecked")] public void TextCheckedExpression(string keywordToken) { - Test($@" -public class Bar -{{ - public void Bar2() - {{ - var i = {keywordToken}(1 + 1); - $$ - }} -}}", -$@" -public class Bar -{{ - public void Bar2() - {{ - var i = {keywordToken}$$(1 +$$ 1)$$ - }} -}}"); + Test($$""" + public class Bar + { + public void Bar2() + { + var i = {{keywordToken}}(1 + 1); + $$ + } + } + """, + $$""" + public class Bar + { + public void Bar2() + { + var i = {{keywordToken}}$$(1 +$$ 1)$$ + } + } + """); } [WpfFact] public void TestConvertFieldToPropertyWithAttributeAndComment() { - Test(@" -public class Bar -{ - public int Property - { - $$ - } + Test(""" + public class Bar + { + public int Property + { + $$ + } - /// - /// - [SomeAttri] - public void Method() { } -}", -@" -public class Bar -{ - public int Property$$ + /// + /// + [SomeAttri] + public void Method() { } + } + """, + """ + public class Bar + { + public int Property$$ - /// - /// - [SomeAttri] - public void Method() { } -}"); + /// + /// + [SomeAttri] + public void Method() { } + } + """); } [WpfFact] public void TestConvertEventFieldToPropertyWithAttributeAndComment() { - Test(@" -public class Bar -{ - public event EventHandler MyEvent - { - $$ - } + Test(""" + public class Bar + { + public event EventHandler MyEvent + { + $$ + } - /// - /// - [SomeAttri] - public void Method() { } -}", -@" -public class Bar -{ - public event EventHandler MyEvent$$ + /// + /// + [SomeAttri] + public void Method() { } + } + """, + """ + public class Bar + { + public event EventHandler MyEvent$$ - /// - /// - [SomeAttri] - public void Method() { } -}"); + /// + /// + [SomeAttri] + public void Method() { } + } + """); } protected override string Language => LanguageNames.CSharp; diff --git a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests_Json.cs b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests_Json.cs index 44c63fe81ea29..f8e81bb763aca 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests_Json.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests_Json.cs @@ -222,6 +222,35 @@ void Goo() Json.Array("]")); } + [Theory, CombinatorialData] + public async Task TestJsonOnApiWithStringSyntaxAttribute_Field_FromLocal(TestHost testHost) + { + await TestAsync( + """ + using System.Diagnostics.CodeAnalysis; + + class Program + { + [StringSyntax(StringSyntaxAttribute.Json)] + private string field; + void Goo() + { + [|var v = @"[{ 'goo': 0}]";|] + this.field = v; + } + } + """ + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, + testHost, + Keyword("var"), + Json.Array("["), + Json.Object("{"), + Json.PropertyName("'goo'"), + Json.Punctuation(":"), + Json.Number("0"), + Json.Object("}"), + Json.Array("]")); + } + [Theory, CombinatorialData] [WorkItem("https://github.com/dotnet/roslyn/issues/74020")] public async Task TestJsonOnApiWithStringSyntaxAttribute_OtherLanguage_Field(TestHost testHost) @@ -302,6 +331,37 @@ void Goo() Json.Array("]")); } + [Theory, CombinatorialData] + public async Task TestJsonOnApiWithStringSyntaxAttribute_Argument_FromLocal(TestHost testHost) + { + await TestAsync( + """ + using System.Diagnostics.CodeAnalysis; + + class Program + { + private void M([StringSyntax(StringSyntaxAttribute.Json)] string p) + { + } + + void Goo() + { + [|var v = @"[{ 'goo': 0}]";|] + M(v); + } + } + """ + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, + testHost, + Keyword("var"), + Json.Array("["), + Json.Object("{"), + Json.PropertyName("'goo'"), + Json.Punctuation(":"), + Json.Number("0"), + Json.Object("}"), + Json.Array("]")); + } + [Theory, CombinatorialData] [WorkItem("https://github.com/dotnet/roslyn/issues/69237")] public async Task TestJsonOnApiWithStringSyntaxAttribute_PropertyInitializer(TestHost testHost) @@ -335,6 +395,40 @@ void Goo() Json.Array("]")); } + [Theory, CombinatorialData] + [WorkItem("https://github.com/dotnet/roslyn/issues/69237")] + public async Task TestJsonOnApiWithStringSyntaxAttribute_PropertyInitializer_FromLocal(TestHost testHost) + { + await TestAsync( + """" + using System.Diagnostics.CodeAnalysis; + + public sealed record Foo + { + [StringSyntax(StringSyntaxAttribute.Json)] + public required string Bar { get; set; } + } + + class Program + { + void Goo() + { + [|var v = """[1, 2, 3]""";|] + var f = new Foo { Bar = v }; + } + } + """" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, + testHost, + Keyword("var"), + Json.Array("["), + Json.Number("1"), + Json.Punctuation(","), + Json.Number("2"), + Json.Punctuation(","), + Json.Number("3"), + Json.Array("]")); + } + [Theory, CombinatorialData] [WorkItem("https://github.com/dotnet/roslyn/issues/69237")] public async Task TestJsonOnApiWithStringSyntaxAttribute_WithExpression(TestHost testHost) @@ -368,4 +462,75 @@ void Goo() Json.Number("3"), Json.Array("]")); } + + [Theory, CombinatorialData] + [WorkItem("https://github.com/dotnet/roslyn/issues/69237")] + public async Task TestJsonOnApiWithStringSyntaxAttribute_WithExpression_FromLocal(TestHost testHost) + { + await TestAsync( + """" + using System.Diagnostics.CodeAnalysis; + + public sealed record Foo + { + [StringSyntax(StringSyntaxAttribute.Json)] + public required string Bar { get; set; } + } + + class Program + { + void Goo() + { + var f = new Foo { Bar = "" }; + [|var v = """[1, 2, 3]""";|] + f = f with { Bar = v }; + } + } + """" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, + testHost, + Keyword("var"), + Json.Array("["), + Json.Number("1"), + Json.Punctuation(","), + Json.Number("2"), + Json.Punctuation(","), + Json.Number("3"), + Json.Array("]")); + } + + [Theory, CombinatorialData] + [WorkItem("https://github.com/dotnet/roslyn/issues/69237")] + public async Task TestJsonOnApiWithStringSyntaxAttribute_WithExpression_FromLocal2(TestHost testHost) + { + await TestAsync( + """" + using System.Diagnostics.CodeAnalysis; + + public sealed record Foo + { + [StringSyntax(StringSyntaxAttribute.Json)] + public required string Bar { get; set; } + } + + class Program + { + void Goo() + { + var f = new Foo { Bar = "" }; + string v; + [|v = """[1, 2, 3]""";|] + f = f with { Bar = v }; + } + } + """" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, + testHost, + Local("v"), + Json.Array("["), + Json.Number("1"), + Json.Punctuation(","), + Json.Number("2"), + Json.Punctuation(","), + Json.Number("3"), + Json.Array("]")); + } } diff --git a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs index 2fbe62118fa0f..e046229974102 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Classification; -public partial class SyntacticClassifierTests : AbstractCSharpClassifierTests +public sealed partial class SyntacticClassifierTests : AbstractCSharpClassifierTests { protected override async Task> GetClassificationSpansAsync(string code, ImmutableArray spans, ParseOptions? options, TestHost testHost) { @@ -30,9 +30,11 @@ protected override async Task> GetClassificationS public async Task VarAtTypeMemberLevel(TestHost testHost) { await TestAsync( -@"class C -{ - var goo }", + """ + class C + { + var goo } + """, testHost, Keyword("class"), Class("C"), @@ -46,9 +48,11 @@ await TestAsync( public async Task TestNamespace(TestHost testHost) { await TestAsync( -@"namespace N -{ -}", + """ + namespace N + { + } + """, testHost, Keyword("namespace"), Namespace("N"), @@ -60,8 +64,10 @@ await TestAsync( public async Task TestFileScopedNamespace(TestHost testHost) { await TestAsync( -@"namespace N; -", + """ + namespace N; + + """, testHost, Keyword("namespace"), Namespace("N"), @@ -117,12 +123,14 @@ await TestInMethodAsync( public async Task VarAsMethodParameter(TestHost testHost) { await TestAsync( -@"class C -{ - void M(var v) - { - } -}", + """ + class C + { + void M(var v) + { + } + } + """, testHost, Keyword("class"), Class("C"), @@ -142,16 +150,18 @@ void M(var v) public async Task YieldYield(TestHost testHost) { await TestAsync( -@"using System.Collections.Generic; + """ + using System.Collections.Generic; -class yield -{ - IEnumerable M() - { - yield yield = new yield(); - yield return yield; - } -}", + class yield + { + IEnumerable M() + { + yield yield = new yield(); + yield return yield; + } + } + """, testHost, Keyword("using"), Identifier("System"), @@ -191,16 +201,18 @@ IEnumerable M() public async Task YieldYieldAsSpans(TestHost testHost) { await TestAsync( -@"using System.Collections.Generic; + """ + using System.Collections.Generic; -class yield -{ - IEnumerable M() - { - [|yield yield = new yield();|] - [|yield return yield;|] - } -}", + class yield + { + IEnumerable M() + { + [|yield yield = new yield();|] + [|yield return yield;|] + } + } + """, testHost, Identifier("yield"), Local("yield"), @@ -230,8 +242,10 @@ public async Task YieldReturn(TestHost testHost) public async Task YieldFixed(TestHost testHost) { await TestInMethodAsync( -@"yield return this.items[0]; yield break; fixed (int* i = 0) { -}", + """ + yield return this.items[0]; yield break; fixed (int* i = 0) { + } + """, testHost, ControlKeyword("yield"), ControlKeyword("return"), @@ -257,6 +271,48 @@ await TestInMethodAsync( Punctuation.CloseCurly); } + [Theory, CombinatorialData] + [WorkItem("https://github.com/dotnet/roslyn/issues/40741")] + public async Task TestAwait(TestHost testHost) + { + await TestAsync( + """ + using System.Threading.Tasks; + + class X + { + async Task M() + { + await M(); + } + } + """, + testHost, + Keyword("using"), + Identifier("System"), + Operators.Dot, + Identifier("Threading"), + Operators.Dot, + Identifier("Tasks"), + Punctuation.Semicolon, + Keyword("class"), + Class("X"), + Punctuation.OpenCurly, + Keyword("async"), + Identifier("Task"), + Method("M"), + Punctuation.OpenParen, + Punctuation.CloseParen, + Punctuation.OpenCurly, + ControlKeyword("await"), + Identifier("M"), + Punctuation.OpenParen, + Punctuation.CloseParen, + Punctuation.Semicolon, + Punctuation.CloseCurly, + Punctuation.CloseCurly); + } + [Theory, CombinatorialData] public async Task PartialClass(TestHost testHost) { @@ -272,9 +328,11 @@ await TestAsync("public partial class Goo", public async Task PartialMethod(TestHost testHost) { await TestInClassAsync( -@"public partial void M() -{ -}", + """ + public partial void M() + { + } + """, testHost, Keyword("public"), Keyword("partial"), @@ -307,17 +365,19 @@ await TestInMethodAsync( public async Task PartialClassStructInterface(TestHost testHost) { await TestAsync( -@"partial class T1 -{ -} + """ + partial class T1 + { + } -partial struct T2 -{ -} + partial struct T2 + { + } -partial interface T3 -{ -}", + partial interface T3 + { + } + """, testHost, Keyword("partial"), Keyword("class"), @@ -356,9 +416,13 @@ await TestInNamespaceAsync(kw + " goo", [Theory, CombinatorialData] public async Task VerbatimStringLiterals1(TestHost testHost) { - await TestInMethodAsync(@"@""goo""", + await TestInMethodAsync(""" + @"goo" + """, testHost, - Verbatim(@"@""goo""")); + Verbatim(""" + @"goo" + """)); } [Theory, CombinatorialData] @@ -366,7 +430,9 @@ public async Task VerbatimStringLiteralsUtf8_01(TestHost testHost) { await TestInMethodAsync(@"@""goo""u8", testHost, - Verbatim(@"@""goo"""), + Verbatim(""" + @"goo" + """), Keyword("u8")); } @@ -375,7 +441,9 @@ public async Task VerbatimStringLiteralsUtf8_02(TestHost testHost) { await TestInMethodAsync(@"@""goo""U8", testHost, - Verbatim(@"@""goo"""), + Verbatim(""" + @"goo" + """), Keyword("U8")); } @@ -385,9 +453,13 @@ await TestInMethodAsync(@"@""goo""U8", [Theory, CombinatorialData] public async Task VerbatimStringLiterals2(TestHost testHost) { - await TestAsync(@"@""", + await TestAsync(""" + @" + """, testHost, - Verbatim(@"@""")); + Verbatim(""" + @" + """)); } /// @@ -396,10 +468,14 @@ await TestAsync(@"@""", [Theory, CombinatorialData] public async Task VerbatimStringLiterals3(TestHost testHost) { - await TestAsync(@"goo @""", + await TestAsync(""" + goo @" + """, testHost, Identifier("goo"), - Verbatim(@"@""")); + Verbatim(""" + @" + """)); } /// @@ -408,32 +484,40 @@ await TestAsync(@"goo @""", [Theory, CombinatorialData] public async Task VerbatimStringLiterals4(TestHost testHost) { - var code = @" + var code = """ -@"" goo bar -"; + @" goo bar + + + """; await TestAsync(code, testHost, - Verbatim(@"@"" goo bar + Verbatim(""" + @" goo bar -")); + + """)); } [Theory, CombinatorialData] public async Task VerbatimStringLiterals5(TestHost testHost) { - var code = @" + var code = """ + -@"" goo bar -and -on a new line "" -more stuff"; + @" goo bar + and + on a new line " + more stuff + """; await TestInMethodAsync(code, testHost, - Verbatim(@"@"" goo bar -and -on a new line """), + Verbatim(""" + @" goo bar + and + on a new line " + """), Identifier("more"), Local("stuff")); } @@ -441,17 +525,21 @@ await TestInMethodAsync(code, [Theory, CombinatorialData] public async Task VerbatimStringLiteralsUtf8_03(TestHost testHost) { - var code = @" + var code = """ -@"" goo bar -and -on a new line ""u8 -more stuff"; + + @" goo bar + and + on a new line "u8 + more stuff + """; await TestInMethodAsync(code, testHost, - Verbatim(@"@"" goo bar -and -on a new line """), + Verbatim(""" + @" goo bar + and + on a new line " + """), Keyword("u8"), Identifier("more"), Local("stuff")); @@ -460,17 +548,21 @@ await TestInMethodAsync(code, [Theory, CombinatorialData] public async Task VerbatimStringLiteralsUtf8_04(TestHost testHost) { - var code = @" + var code = """ + -@"" goo bar -and -on a new line ""U8 -more stuff"; + @" goo bar + and + on a new line "U8 + more stuff + """; await TestInMethodAsync(code, testHost, - Verbatim(@"@"" goo bar -and -on a new line """), + Verbatim(""" + @" goo bar + and + on a new line " + """), Keyword("U8"), Identifier("more"), Local("stuff")); @@ -492,7 +584,9 @@ await TestAsync( Keyword("string"), script ? Field("s") : Local("s"), Operators.Equals, - Verbatim(@"@""""""/*"""), + Verbatim("""" + @"""/*" + """"), Punctuation.Semicolon); } @@ -511,7 +605,9 @@ await TestAsync( Keyword("string"), script ? Field("s") : Local("s"), Operators.Equals, - Verbatim(@"@""""""/*"""), + Verbatim("""" + @"""/*" + """"), Keyword("u8"), Punctuation.Semicolon); } @@ -531,7 +627,9 @@ await TestAsync( Keyword("string"), script ? Field("s") : Local("s"), Operators.Equals, - Verbatim(@"@""""""/*"""), + Verbatim("""" + @"""/*" + """"), Keyword("u8"), Punctuation.Semicolon); } @@ -539,52 +637,76 @@ await TestAsync( [Theory, CombinatorialData] public async Task StringLiteral1(TestHost testHost) { - await TestAsync(@"""goo""", + await TestAsync(""" + "goo" + """, testHost, - String(@"""goo""")); + String(""" + "goo" + """)); } [Theory, CombinatorialData] public async Task StringLiteralUtf8_01(TestHost testHost) { - await TestAsync(@"""goo""u8", + await TestAsync(""" + "goo"u8 + """, testHost, - String(@"""goo"""), + String(""" + "goo" + """), Keyword("u8")); } [Theory, CombinatorialData] public async Task StringLiteralUtf8_02(TestHost testHost) { - await TestAsync(@"""goo""U8", + await TestAsync(""" + "goo"U8 + """, testHost, - String(@"""goo"""), + String(""" + "goo" + """), Keyword("U8")); } [Theory, CombinatorialData] public async Task StringLiteral2(TestHost testHost) { - await TestAsync(@"""""", + await TestAsync(""" + "" + """, testHost, - String(@"""""")); + String(""" + "" + """)); } [Theory, CombinatorialData] public async Task StringLiteralUtf8_03(TestHost testHost) { - await TestAsync(@"""""u8", + await TestAsync(""" + ""u8 + """, testHost, - String(@""""""), + String(""" + "" + """), Keyword("u8")); } [Theory, CombinatorialData] public async Task StringLiteralUtf8_04(TestHost testHost) { - await TestAsync(@"""""U8", + await TestAsync(""" + ""U8 + """, testHost, - String(@""""""), + String(""" + "" + """), Keyword("U8")); } @@ -667,7 +789,9 @@ await TestInExpressionAsync(code, [Theory, CombinatorialData] public async Task LinqWhere2(TestHost testHost) { - var code = @"from it in goo where it > ""bar"""; + var code = """ + from it in goo where it > "bar" + """; await TestInExpressionAsync(code, testHost, Keyword("from"), @@ -677,7 +801,9 @@ await TestInExpressionAsync(code, Keyword("where"), Identifier("it"), Operators.GreaterThan, - String(@"""bar""")); + String(""" + "bar" + """)); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/44423")] @@ -704,13 +830,15 @@ await TestAsync(code, public async Task LinqKeywordsAtNamespaceLevel(bool script, TestHost testHost) { // the contextual keywords are actual keywords since we parse top level field declaration and only give a semantic error - var code = @"object goo = from goo in goo - join goo in goo on goo equals goo - group goo by goo into goo - let goo = goo - where goo - orderby goo ascending, goo descending - select goo;"; + var code = """ + object goo = from goo in goo + join goo in goo on goo equals goo + group goo by goo into goo + let goo = goo + where goo + orderby goo ascending, goo descending + select goo; + """; var parseOptions = script ? Options.Script : null; @@ -761,10 +889,12 @@ await TestAsync( public async Task ContextualKeywordsAsFieldName(TestHost testHost) { await TestAsync( -@"class C -{ - int yield, get, set, value, add, remove, global, partial, where, alias; -}", + """ + class C + { + int yield, get, set, value, add, remove, global, partial, where, alias; + } + """, testHost, Keyword("class"), Class("C"), @@ -797,16 +927,18 @@ await TestAsync( public async Task LinqKeywordsInFieldInitializer(TestHost testHost) { await TestAsync( -@"class C -{ - int a = from a in a - join a in a on a equals a - group a by a into a - let a = a - where a - orderby a ascending, a descending - select a; -}", + """ + class C + { + int a = from a in a + join a in a on a equals a + group a by a into a + let a = a + where a + orderby a ascending, a descending + select a; + } + """, testHost, Keyword("class"), Class("C"), @@ -854,58 +986,60 @@ where a public async Task LinqKeywordsAsTypeName(TestHost testHost) { await TestAsync( -@"class var -{ -} + """ + class var + { + } -struct from -{ -} + struct from + { + } -interface join -{ -} + interface join + { + } -enum on -{ -} + enum on + { + } -delegate equals { } -class group -{ -} + delegate equals { } + class group + { + } -class by -{ -} + class by + { + } -class into -{ -} + class into + { + } -class let -{ -} + class let + { + } -class where -{ -} + class where + { + } -class orderby -{ -} + class orderby + { + } -class ascending -{ -} + class ascending + { + } -class descending -{ -} + class descending + { + } -class select -{ -}", + class select + { + } + """, testHost, Keyword("class"), Class("var"), @@ -969,12 +1103,14 @@ class select public async Task LinqKeywordsAsMethodParameters(TestHost testHost) { await TestAsync( -@"class C -{ - orderby M(var goo, from goo, join goo, on goo, equals goo, group goo, by goo, into goo, let goo, where goo, orderby goo, ascending goo, descending goo, select goo) - { - } -}", + """ + class C + { + orderby M(var goo, from goo, join goo, on goo, equals goo, group goo, by goo, into goo, let goo, where goo, orderby goo, ascending goo, descending goo, select goo) + { + } + } + """, testHost, Keyword("class"), Class("C"), @@ -1033,24 +1169,26 @@ orderby M(var goo, from goo, join goo, on goo, equals goo, group goo, by goo, in public async Task LinqKeywordsInLocalVariableDeclarations(TestHost testHost) { await TestAsync( -@"class C -{ - void M() - { - var goo = (var)goo as var; - from goo = (from)goo as from; - join goo = (join)goo as join; - on goo = (on)goo as on; - equals goo = (equals)goo as equals; - group goo = (group)goo as group; - by goo = (by)goo as by; - into goo = (into)goo as into; - orderby goo = (orderby)goo as orderby; - ascending goo = (ascending)goo as ascending; - descending goo = (descending)goo as descending; - select goo = (select)goo as select; - } -}", + """ + class C + { + void M() + { + var goo = (var)goo as var; + from goo = (from)goo as from; + join goo = (join)goo as join; + on goo = (on)goo as on; + equals goo = (equals)goo as equals; + group goo = (group)goo as group; + by goo = (by)goo as by; + into goo = (into)goo as into; + orderby goo = (orderby)goo as orderby; + ascending goo = (ascending)goo as ascending; + descending goo = (descending)goo as descending; + select goo = (select)goo as select; + } + } + """, testHost, Keyword("class"), Class("C"), @@ -1188,10 +1326,12 @@ void M() public async Task LinqKeywordsAsFieldNames(TestHost testHost) { await TestAsync( -@"class C -{ - int var, from, join, on, into, equals, let, orderby, ascending, descending, select, group, by, partial; -}", + """ + class C + { + int var, from, join, on, into, equals, let, orderby, ascending, descending, select, group, by, partial; + } + """, testHost, Keyword("class"), Class("C"), @@ -1232,11 +1372,13 @@ await TestAsync( public async Task LinqKeywordsAtFieldLevelInvalid(TestHost testHost) { await TestAsync( -@"class C -{ - string Property { from a in a join a in a on a equals a group a by a into a let a = a where a orderby a ascending, -a descending select a; } -}", + """ + class C + { + string Property { from a in a join a in a on a equals a group a by a into a let a = a where a orderby a ascending, + a descending select a; } + } + """, testHost, Keyword("class"), Class("C"), @@ -1306,11 +1448,13 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task CommentAsLeadingTrivia1(TestHost testHost) { - var code = @" -class Bar { - // goo - void Method1() { } -}"; + var code = """ + + class Bar { + // goo + void Method1() { } + } + """; await TestAsync(code, testHost, Keyword("class"), @@ -1329,8 +1473,10 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task ShebangAsFirstCommentInScript(TestHost testHost) { - var code = @"#!/usr/bin/env scriptcs -System.Console.WriteLine();"; + var code = """ + #!/usr/bin/env scriptcs + System.Console.WriteLine(); + """; var expected = new[] { @@ -1351,8 +1497,10 @@ public async Task ShebangAsFirstCommentInScript(TestHost testHost) [Theory, CombinatorialData] public async Task ShebangAsFirstCommentInNonScript(TestHost testHost) { - var code = @"#!/usr/bin/env scriptcs -System.Console.WriteLine();"; + var code = """ + #!/usr/bin/env scriptcs + System.Console.WriteLine(); + """; var expected = new[] { @@ -1374,8 +1522,10 @@ public async Task ShebangAsFirstCommentInNonScript(TestHost testHost) [Theory, CombinatorialData] public async Task ShebangNotAsFirstCommentInScript(TestHost testHost) { - var code = @" #!/usr/bin/env scriptcs -System.Console.WriteLine();"; + var code = """ + #!/usr/bin/env scriptcs + System.Console.WriteLine(); + """; var expected = new[] { @@ -1397,12 +1547,14 @@ public async Task ShebangNotAsFirstCommentInScript(TestHost testHost) [Theory, CombinatorialData] public async Task CommentAsMethodBodyContent(TestHost testHost) { - var code = @" -class Bar { - void Method1() { -// goo -} -}"; + var code = """ + + class Bar { + void Method1() { + // goo + } + } + """; await TestAsync(code, testHost, Keyword("class"), @@ -1422,11 +1574,13 @@ await TestAsync(code, public async Task CommentMix1(TestHost testHost) { await TestAsync( -@"// comment1 /* -class cl -{ -} -//comment2 */", + """ + // comment1 /* + class cl + { + } + //comment2 */ + """, testHost, Comment("// comment1 /*"), Keyword("class"), @@ -1454,9 +1608,11 @@ await TestInMethodAsync( [Theory, CombinatorialData] public async Task XmlDocCommentOnClass(TestHost testHost) { - var code = @" -/// something -class Bar { }"; + var code = """ + + /// something + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1477,11 +1633,13 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocCommentOnClassWithIndent(TestHost testHost) { - var code = @" - /// - /// something - /// - class Bar { }"; + var code = """ + + /// + /// something + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1505,9 +1663,11 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_EntityReference(TestHost testHost) { - var code = @" -/// A -class Bar { }"; + var code = """ + + /// A + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1528,10 +1688,12 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_ExteriorTriviaInsideCloseTag(TestHost testHost) { - var code = @" -/// something -class Bar { }"; + var code = """ + + /// something + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1554,12 +1716,14 @@ await TestAsync(code, [CombinatorialData] public async Task XmlDocComment_ExteriorTriviaInsideCRef(TestHost testHost) { - var code = @" -/// -class C -{ -}"; + var code = """ + + /// + class C + { + } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1568,12 +1732,16 @@ await TestAsync(code, XmlDoc.Name("see"), XmlDoc.AttributeName("cref"), XmlDoc.Delimiter("="), - XmlDoc.AttributeQuotes("\""), + XmlDoc.AttributeQuotes(""" + " + """), Identifier("System"), Operators.Dot, XmlDoc.Delimiter("///"), Identifier("Int32"), - XmlDoc.AttributeQuotes("\""), + XmlDoc.AttributeQuotes(""" + " + """), XmlDoc.Delimiter("/>"), Keyword("class"), Class("C"), @@ -1584,11 +1752,13 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocCommentOnClassWithExteriorTrivia(TestHost testHost) { - var code = @" -/// -/// something -/// -class Bar { }"; + var code = """ + + /// + /// something + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1613,10 +1783,12 @@ await TestAsync(code, public async Task XmlDocComment_ExteriorTriviaNoText(TestHost testHost) { var code = -@"/// -///something -/// -class Bar { }"; + """ + /// + ///something + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1638,9 +1810,11 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_EmptyElement(TestHost testHost) { - var code = @" -/// -class Bar { }"; + var code = """ + + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1657,9 +1831,11 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_Attribute(TestHost testHost) { - var code = @" -/// something -class Bar { }"; + var code = """ + + /// something + class Bar { } + """; await TestAsync(code, testHost, @@ -1669,9 +1845,13 @@ await TestAsync(code, XmlDoc.Name("summary"), XmlDoc.AttributeName("attribute"), XmlDoc.Delimiter("="), - XmlDoc.AttributeQuotes(@""""), + XmlDoc.AttributeQuotes(""" + " + """), XmlDoc.AttributeValue(@"value"), - XmlDoc.AttributeQuotes(@""""), + XmlDoc.AttributeQuotes(""" + " + """), XmlDoc.Delimiter(">"), XmlDoc.Text("something"), XmlDoc.Delimiter(" -class Bar { }"; + var code = """ + + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1697,9 +1879,13 @@ await TestAsync(code, XmlDoc.Name("summary"), XmlDoc.AttributeName("attribute"), XmlDoc.Delimiter("="), - XmlDoc.AttributeQuotes(@""""), + XmlDoc.AttributeQuotes(""" + " + """), XmlDoc.AttributeValue(@"value"), - XmlDoc.AttributeQuotes(@""""), + XmlDoc.AttributeQuotes(""" + " + """), XmlDoc.Delimiter("/>"), Keyword("class"), Class("Bar"), @@ -1710,9 +1896,11 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_ExtraSpaces(TestHost testHost) { - var code = @" -/// < summary attribute = ""value"" /> -class Bar { }"; + var code = """ + + /// < summary attribute = "value" /> + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1721,9 +1909,13 @@ await TestAsync(code, XmlDoc.Name("summary"), XmlDoc.AttributeName("attribute"), XmlDoc.Delimiter("="), - XmlDoc.AttributeQuotes(@""""), + XmlDoc.AttributeQuotes(""" + " + """), XmlDoc.AttributeValue(@"value"), - XmlDoc.AttributeQuotes(@""""), + XmlDoc.AttributeQuotes(""" + " + """), XmlDoc.Delimiter("/>"), Keyword("class"), Class("Bar"), @@ -1734,9 +1926,11 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_XmlComment(TestHost testHost) { - var code = @" -/// -class Bar { }"; + var code = """ + + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1752,10 +1946,12 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_XmlCommentWithExteriorTrivia(TestHost testHost) { - var code = @" -/// -class Bar { }"; + var code = """ + + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1773,9 +1969,11 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_XmlCommentInElement(TestHost testHost) { - var code = @" -/// -class Bar { }"; + var code = """ + + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1798,11 +1996,13 @@ await TestAsync(code, [WorkItem("https://github.com/dotnet/roslyn/pull/31410")] public async Task XmlDocComment_MalformedXmlDocComment(TestHost testHost) { - var code = @" -/// -///. -/// -class C { }"; + var code = """ + + /// + ///. + /// + class C { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1832,10 +2032,12 @@ await TestAsync(code, public async Task MultilineXmlDocComment_ExteriorTrivia(TestHost testHost) { var code = -@"/** -*comment -**/ -class Bar { }"; + """ + /** + *comment + **/ + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("/**"), @@ -1858,10 +2060,12 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task XmlDocComment_CDataWithExteriorTrivia(TestHost testHost) { - var code = @" -/// -class Bar { }"; + var code = """ + + /// + class Bar { } + """; await TestAsync(code, testHost, XmlDoc.Delimiter("///"), @@ -1880,14 +2084,16 @@ await TestAsync(code, public async Task XmlDocComment_ProcessingDirective(TestHost testHost) { await TestAsync( -@"/// -public class Program -{ - static void Main() - { - } -}", + """ + /// + public class Program + { + static void Main() + { + } + } + """, testHost, XmlDoc.Delimiter("///"), XmlDoc.Text(" "), @@ -2142,10 +2348,12 @@ await TestAsync(code, public async Task GenericParameter_Method(TestHost testHost) { await TestInClassAsync( -@"T M(T t) -{ - return default(T); -}", + """ + T M(T t) + { + return default(T); + } + """, testHost, Identifier("T"), Method("M"), @@ -2182,11 +2390,13 @@ await TestInExpressionAsync("true ? 1 : 0", public async Task BaseClass(TestHost testHost) { await TestAsync( -@"class C : B -{ -}", - testHost, - Keyword("class"), + """ + class C : B + { + } + """, + testHost, + Keyword("class"), Class("C"), Punctuation.Colon, Identifier("B"), @@ -2220,9 +2430,11 @@ await TestAsync( public async Task TestAngleBracketsOnGenericConstraints_Bug932262(TestHost testHost) { await TestAsync( -@"class C where T : A -{ -}", + """ + class C where T : A + { + } + """, testHost, Keyword("class"), Class("C"), @@ -2316,9 +2528,11 @@ await TestAsync( public async Task AttributeTargetSpecifiersOnDelegate(TestHost testHost) { await TestInClassAsync( -@"[type: A] -[return: A] -delegate void M();", + """ + [type: A] + [return: A] + delegate void M(); + """, testHost, Punctuation.OpenBracket, Keyword("type"), @@ -2342,11 +2556,13 @@ await TestInClassAsync( public async Task AttributeTargetSpecifiersOnMethod(TestHost testHost) { await TestInClassAsync( -@"[return: A] -[method: A] -void M() -{ -}", + """ + [return: A] + [method: A] + void M() + { + } + """, testHost, Punctuation.OpenBracket, Keyword("return"), @@ -2370,13 +2586,15 @@ void M() public async Task AttributeTargetSpecifiersOnCtor(TestHost testHost) { await TestAsync( -@"class C -{ - [method: A] - C() - { - } -}", + """ + class C + { + [method: A] + C() + { + } + } + """, testHost, Keyword("class"), Class("C"), @@ -2398,13 +2616,15 @@ await TestAsync( public async Task AttributeTargetSpecifiersOnDtor(TestHost testHost) { await TestAsync( -@"class C -{ - [method: A] - ~C() - { - } -}", + """ + class C + { + [method: A] + ~C() + { + } + } + """, testHost, Keyword("class"), Class("C"), @@ -2427,11 +2647,13 @@ await TestAsync( public async Task AttributeTargetSpecifiersOnOperator(TestHost testHost) { await TestInClassAsync( -@"[method: A] -[return: A] -static T operator +(T a, T b) -{ -}", + """ + [method: A] + [return: A] + static T operator +(T a, T b) + { + } + """, testHost, Punctuation.OpenBracket, Keyword("method"), @@ -2462,21 +2684,23 @@ await TestInClassAsync( public async Task AttributeTargetSpecifiersOnEventDeclaration(TestHost testHost) { await TestInClassAsync( -@"[event: A] -event A E -{ - [param: Test] - [method: Test] - add - { - } + """ + [event: A] + event A E + { + [param: Test] + [method: Test] + add + { + } - [param: Test] - [method: Test] - remove - { - } -}", + [param: Test] + [method: Test] + remove + { + } + } + """, testHost, Punctuation.OpenBracket, Keyword("event"), @@ -2520,20 +2744,22 @@ event A E public async Task AttributeTargetSpecifiersOnPropertyAccessors(TestHost testHost) { await TestInClassAsync( -@"int P -{ - [return: T] - [method: T] - get - { - } + """ + int P + { + [return: T] + [method: T] + get + { + } - [param: T] - [method: T] - set - { - } -}", + [param: T] + [method: T] + set + { + } + } + """, testHost, Keyword("int"), Property("P"), @@ -2571,8 +2797,10 @@ await TestInClassAsync( public async Task AttributeTargetSpecifiersOnIndexers(TestHost testHost) { await TestInClassAsync( -@"[property: A] -int this[int i] { get; set; }", + """ + [property: A] + int this[int i] { get; set; } + """, testHost, Punctuation.OpenBracket, Keyword("property"), @@ -2597,20 +2825,22 @@ await TestInClassAsync( public async Task AttributeTargetSpecifiersOnIndexerAccessors(TestHost testHost) { await TestInClassAsync( -@"int this[int i] -{ - [return: T] - [method: T] - get - { - } + """ + int this[int i] + { + [return: T] + [method: T] + get + { + } - [param: T] - [method: T] - set - { - } -}", + [param: T] + [method: T] + set + { + } + } + """, testHost, Keyword("int"), Keyword("this"), @@ -2652,8 +2882,10 @@ await TestInClassAsync( public async Task AttributeTargetSpecifiersOnField(TestHost testHost) { await TestInClassAsync( -@"[field: A] -const int a = 0;", + """ + [field: A] + const int a = 0; + """, testHost, Punctuation.OpenBracket, Keyword("field"), @@ -2673,189 +2905,191 @@ await TestInClassAsync( public async Task TestAllKeywords(TestHost testHost) { await TestAsync( -@"using System; -#region TaoRegion -namespace MyNamespace -{ - abstract class Goo : Bar - { - bool goo = default(bool); - byte goo1; - char goo2; - const int goo3 = 999; - decimal goo4; - - delegate void D(); - delegate* managed mgdfun; - delegate* unmanaged unmgdfun; - - double goo5; - - enum MyEnum - { - one, - two, - three - }; - - event D MyEvent; - - float goo6; - static int x; - long goo7; - sbyte goo8; - short goo9; - int goo10 = sizeof(int); - string goo11; - uint goo12; - ulong goo13; - volatile ushort goo14; - - struct SomeStruct - { - } - - protected virtual void someMethod() - { - } - - public Goo(int i) - { - bool var = i is int; - try + """ + using System; + #region TaoRegion + namespace MyNamespace { - while (true) + abstract class Goo : Bar { - continue; - break; + bool goo = default(bool); + byte goo1; + char goo2; + const int goo3 = 999; + decimal goo4; + + delegate void D(); + delegate* managed mgdfun; + delegate* unmanaged unmgdfun; + + double goo5; + + enum MyEnum + { + one, + two, + three + }; + + event D MyEvent; + + float goo6; + static int x; + long goo7; + sbyte goo8; + short goo9; + int goo10 = sizeof(int); + string goo11; + uint goo12; + ulong goo13; + volatile ushort goo14; + + struct SomeStruct + { + } + + protected virtual void someMethod() + { + } + + public Goo(int i) + { + bool var = i is int; + try + { + while (true) + { + continue; + break; + } + + switch (goo) + { + case true: + break; + default: + break; + } + } + catch (System.Exception) + { + } + finally + { + } + + checked + { + int i2 = 10000; + i2++; + } + + do + { + } + while (true); + if (false) + { + } + else + { + } + + unsafe + { + fixed (int* p = &x) + { + } + + char* buffer = stackalloc char[16]; + } + + for (int i1 = 0; i1 < 10; i1++) + { + } + + System.Collections.ArrayList al = new System.Collections.ArrayList(); + foreach (object o in al) + { + object o1 = o; + } + + lock (this) + { + } + } + + Goo method(Bar i, out int z) + { + z = 5; + return i as Goo; + } + + public static explicit operator Goo(int i) + { + return new Baz(1); + } + + public static implicit operator Goo(double x) + { + return new Baz(1); + } + + public extern void doSomething(); + + internal void method2(object o) + { + if (o == null) + goto Output; + if (o is Baz) + return; + else + throw new System.Exception(); + Output: + Console.WriteLine("Finished"); + } } - switch (goo) + sealed class Baz : Goo { - case true: - break; - default: - break; + readonly int field; + + public Baz(int i) : base(i) + { + } + + public void someOtherMethod(ref int i, System.Type c) + { + int f = 1; + someOtherMethod(ref f, typeof(int)); + } + + protected override void someMethod() + { + unchecked + { + int i = 1; + i++; + } + } + + private void method(params object[] args) + { + } + + private string aMethod(object o) => o switch + { + int => string.Empty, + _ when true => throw new System.Exception() + }; } - } - catch (System.Exception) - { - } - finally - { - } - checked - { - int i2 = 10000; - i2++; - } - - do - { - } - while (true); - if (false) - { - } - else - { - } - - unsafe - { - fixed (int* p = &x) + interface Bar { } - - char* buffer = stackalloc char[16]; - } - - for (int i1 = 0; i1 < 10; i1++) - { - } - - System.Collections.ArrayList al = new System.Collections.ArrayList(); - foreach (object o in al) - { - object o1 = o; - } - - lock (this) - { - } - } - - Goo method(Bar i, out int z) - { - z = 5; - return i as Goo; - } - - public static explicit operator Goo(int i) - { - return new Baz(1); - } - - public static implicit operator Goo(double x) - { - return new Baz(1); - } - - public extern void doSomething(); - - internal void method2(object o) - { - if (o == null) - goto Output; - if (o is Baz) - return; - else - throw new System.Exception(); - Output: - Console.WriteLine(""Finished""); - } - } - - sealed class Baz : Goo - { - readonly int field; - - public Baz(int i) : base(i) - { - } - - public void someOtherMethod(ref int i, System.Type c) - { - int f = 1; - someOtherMethod(ref f, typeof(int)); - } - - protected override void someMethod() - { - unchecked - { - int i = 1; - i++; } - } - - private void method(params object[] args) - { - } - - private string aMethod(object o) => o switch - { - int => string.Empty, - _ when true => throw new System.Exception() - }; - } - - interface Bar - { - } -} -#endregion TaoRegion", + #endregion TaoRegion + """, testHost, [new CSharpParseOptions(LanguageVersion.CSharp8)], Keyword("using"), @@ -3252,7 +3486,9 @@ [new CSharpParseOptions(LanguageVersion.CSharp8)], Operators.Dot, Identifier("WriteLine"), Punctuation.OpenParen, - String(@"""Finished"""), + String(""" + "Finished" + """), Punctuation.CloseParen, Punctuation.Semicolon, Punctuation.CloseCurly, @@ -3387,60 +3623,62 @@ [new CSharpParseOptions(LanguageVersion.CSharp8)], public async Task TestAllOperators(TestHost testHost) { await TestAsync( -@"using IO = System.IO; - -public class Goo -{ - public void method() - { - int[] a = new int[5]; - int[] var = { - 1, - 2, - 3, - 4, - 5 - }; - int i = a[i]; - Goo f = new Goo(); - f.method(); - i = i + i - i * i / i % i & i | i ^ i; - bool b = true & false | true ^ false; - b = !b; - i = ~i; - b = i < i && i > i; - int? ii = 5; - int f = true ? 1 : 0; - i++; - i--; - b = true && false || true; - i << 5; - i >> 5; - i >>> 5; - b = i == i && i != i && i <= i && i >= i; - i += 5.0; - i -= i; - i *= i; - i /= i; - i %= i; - i &= i; - i |= i; - i ^= i; - i <<= i; - i >>= i; - i >>>= i; - i ??= i; - object s = x => x + 1; - Point point; - unsafe - { - Point* p = &point; - p->x = 10; - } + """ + using IO = System.IO; - IO::BinaryReader br = null; - } -}", + public class Goo + { + public void method() + { + int[] a = new int[5]; + int[] var = { + 1, + 2, + 3, + 4, + 5 + }; + int i = a[i]; + Goo f = new Goo(); + f.method(); + i = i + i - i * i / i % i & i | i ^ i; + bool b = true & false | true ^ false; + b = !b; + i = ~i; + b = i < i && i > i; + int? ii = 5; + int f = true ? 1 : 0; + i++; + i--; + b = true && false || true; + i << 5; + i >> 5; + i >>> 5; + b = i == i && i != i && i <= i && i >= i; + i += 5.0; + i -= i; + i *= i; + i /= i; + i %= i; + i &= i; + i |= i; + i ^= i; + i <<= i; + i >>= i; + i >>>= i; + i ??= i; + object s = x => x + 1; + Point point; + unsafe + { + Point* p = &point; + p->x = 10; + } + + IO::BinaryReader br = null; + } + } + """, testHost, Keyword("using"), Identifier("IO"), @@ -3719,22 +3957,24 @@ public void method() public async Task TestPartialMethodWithNamePartial(TestHost testHost) { await TestAsync( -@"partial class C -{ - partial void partial(string bar); + """ + partial class C + { + partial void partial(string bar); - partial void partial(string baz) - { - } + partial void partial(string baz) + { + } - partial int Goo(); + partial int Goo(); - partial int Goo() - { - } + partial int Goo() + { + } - public partial void partial void -}", + public partial void partial void + } + """, testHost, Keyword("partial"), Keyword("class"), @@ -3782,16 +4022,18 @@ public partial void partial void public async Task ValueInSetterAndAnonymousTypePropertyName(TestHost testHost) { await TestAsync( -@"class C -{ - int P - { - set - { - var t = new { value = value }; - } - } -}", + """ + class C + { + int P + { + set + { + var t = new { value = value }; + } + } + } + """, testHost, Keyword("class"), Class("C"), @@ -3821,17 +4063,19 @@ int P public async Task TestValueInLabel(TestHost testHost) { await TestAsync( -@"class C -{ - int X - { - set - { - value: - ; - } - } -}", + """ + class C + { + int X + { + set + { + value: + ; + } + } + } + """, testHost, Keyword("class"), Class("C"), @@ -3854,19 +4098,21 @@ int X public async Task TestGenericVar(TestHost testHost) { await TestAsync( -@"using System; + """ + using System; -static class Program -{ - static void Main() - { - var x = 1; - } -} + static class Program + { + static void Main() + { + var x = 1; + } + } -class var -{ -}", + class var + { + } + """, testHost, Keyword("using"), Identifier("System"), @@ -3904,22 +4150,24 @@ class var public async Task TestInaccessibleVar(TestHost testHost) { await TestAsync( -@"using System; + """ + using System; -class A -{ - private class var - { - } -} + class A + { + private class var + { + } + } -class B : A -{ - static void Main() - { - var x = 1; - } -}", + class B : A + { + static void Main() + { + var x = 1; + } + } + """, testHost, Keyword("using"), Identifier("System"), @@ -3959,13 +4207,15 @@ static void Main() public async Task TestEscapedVar(TestHost testHost) { await TestAsync( -@"class Program -{ - static void Main(string[] args) - { - @var v = 1; - } -}", + """ + class Program + { + static void Main(string[] args) + { + @var v = 1; + } + } + """, testHost, Keyword("class"), Class("Program"), @@ -3995,23 +4245,25 @@ static void Main(string[] args) public async Task TestVar(TestHost testHost) { await TestAsync( -@"class Program -{ - class var - { - } + """ + class Program + { + class var + { + } - static var GetVarT() - { - return null; - } + static var GetVarT() + { + return null; + } - static void Main() - { - var x = GetVarT(); - var y = new var(); - } -}", + static void Main() + { + var x = GetVarT(); + var y = new var(); + } + } + """, testHost, Keyword("class"), Class("Program"), @@ -4071,15 +4323,17 @@ static void Main() public async Task TestVar2(TestHost testHost) { await TestAsync( -@"class Program -{ - void Main(string[] args) - { - foreach (var v in args) - { - } - } -}", + """ + class Program + { + void Main(string[] args) + { + foreach (var v in args) + { + } + } + } + """, testHost, Keyword("class"), Class("Program"), @@ -4109,53 +4363,69 @@ void Main(string[] args) [Theory, CombinatorialData] public async Task InterpolatedStrings1(TestHost testHost) { - var code = @" -var x = ""World""; -var y = $""Hello, {x}""; -"; + var code = """ + + var x = "World"; + var y = $"Hello, {x}"; + + """; await TestInMethodAsync(code, testHost, Keyword("var"), Local("x"), Operators.Equals, - String("\"World\""), + String(""" + "World" + """), Punctuation.Semicolon, Keyword("var"), Local("y"), Operators.Equals, - String("$\""), + String(""" + $" + """), String("Hello, "), Punctuation.OpenCurly, Identifier("x"), Punctuation.CloseCurly, - String("\""), + String(""" + " + """), Punctuation.Semicolon); } [Theory, CombinatorialData] public async Task InterpolatedStrings2(TestHost testHost) { - var code = @" -var a = ""Hello""; -var b = ""World""; -var c = $""{a}, {b}""; -"; + var code = """ + + var a = "Hello"; + var b = "World"; + var c = $"{a}, {b}"; + + """; await TestInMethodAsync(code, testHost, Keyword("var"), Local("a"), Operators.Equals, - String("\"Hello\""), + String(""" + "Hello" + """), Punctuation.Semicolon, Keyword("var"), Local("b"), Operators.Equals, - String("\"World\""), + String(""" + "World" + """), Punctuation.Semicolon, Keyword("var"), Local("c"), Operators.Equals, - String("$\""), + String(""" + $" + """), Punctuation.OpenCurly, Identifier("a"), Punctuation.CloseCurly, @@ -4163,34 +4433,44 @@ await TestInMethodAsync(code, Punctuation.OpenCurly, Identifier("b"), Punctuation.CloseCurly, - String("\""), + String(""" + " + """), Punctuation.Semicolon); } [Theory, CombinatorialData] public async Task InterpolatedStrings3(TestHost testHost) { - var code = @" -var a = ""Hello""; -var b = ""World""; -var c = $@""{a}, {b}""; -"; + var code = """ + + var a = "Hello"; + var b = "World"; + var c = $@"{a}, {b}"; + + """; await TestInMethodAsync(code, testHost, Keyword("var"), Local("a"), Operators.Equals, - String("\"Hello\""), + String(""" + "Hello" + """), Punctuation.Semicolon, Keyword("var"), Local("b"), Operators.Equals, - String("\"World\""), + String(""" + "World" + """), Punctuation.Semicolon, Keyword("var"), Local("c"), Operators.Equals, - Verbatim("$@\""), + Verbatim(""" + $@" + """), Punctuation.OpenCurly, Identifier("a"), Punctuation.CloseCurly, @@ -4198,21 +4478,25 @@ await TestInMethodAsync(code, Punctuation.OpenCurly, Identifier("b"), Punctuation.CloseCurly, - Verbatim("\""), + Verbatim(""" + " + """), Punctuation.Semicolon); } [Theory, CombinatorialData] public async Task ExceptionFilter1(TestHost testHost) { - var code = @" -try -{ -} -catch when (true) -{ -} -"; + var code = """ + + try + { + } + catch when (true) + { + } + + """; await TestInMethodAsync(code, testHost, ControlKeyword("try"), @@ -4230,14 +4514,16 @@ await TestInMethodAsync(code, [Theory, CombinatorialData] public async Task ExceptionFilter2(TestHost testHost) { - var code = @" -try -{ -} -catch (System.Exception) when (true) -{ -} -"; + var code = """ + + try + { + } + catch (System.Exception) when (true) + { + } + + """; await TestInMethodAsync(code, testHost, ControlKeyword("try"), @@ -4260,8 +4546,10 @@ await TestInMethodAsync(code, [Theory, CombinatorialData] public async Task OutVar(TestHost testHost) { - var code = @" -F(out var);"; + var code = """ + + F(out var); + """; await TestInMethodAsync(code, testHost, Identifier("F"), @@ -4275,35 +4563,45 @@ await TestInMethodAsync(code, [Theory, CombinatorialData] public async Task ReferenceDirective(TestHost testHost) { - var code = @" -#r ""file.dll"""; + var code = """ + + #r "file.dll" + """; await TestAsync(code, testHost, PPKeyword("#"), PPKeyword("r"), - String("\"file.dll\"")); + String(""" + "file.dll" + """)); } [Theory, CombinatorialData] public async Task LoadDirective(TestHost testHost) { - var code = @" -#load ""file.csx"""; + var code = """ + + #load "file.csx" + """; await TestAsync(code, testHost, PPKeyword("#"), PPKeyword("load"), - String("\"file.csx\"")); + String(""" + "file.csx" + """)); } [Theory, CombinatorialData] public async Task IncompleteAwaitInNonAsyncContext(TestHost testHost) { - var code = @" -void M() -{ - var x = await -}"; + var code = """ + + void M() + { + var x = await + } + """; await TestInClassAsync(code, testHost, Keyword("void"), @@ -4321,11 +4619,13 @@ await TestInClassAsync(code, [Theory, CombinatorialData] public async Task CompleteAwaitInNonAsyncContext(TestHost testHost) { - var code = @" -void M() -{ - var x = await; -}"; + var code = """ + + void M() + { + var x = await; + } + """; await TestInClassAsync(code, testHost, Keyword("void"), @@ -4411,14 +4711,16 @@ await TestInMethodAsync("var values = (a: 1, b: 2)", public async Task TestConflictMarkers1(TestHost testHost) { await TestAsync( -@"class C -{ -<<<<<<< Start - public void Goo(); -======= - public void Bar(); ->>>>>>> End -}", + """ + class C + { + <<<<<<< Start + public void Goo(); + ======= + public void Bar(); + >>>>>>> End + } + """, testHost, Keyword("class"), Class("C"), @@ -4445,16 +4747,18 @@ await TestAsync( public async Task TestConflictMarkers2(TestHost testHost) { await TestAsync( -@"class C -{ -<<<<<<< Start - public void Goo(); -||||||| Baseline - int removed; -======= - public void Bar(); ->>>>>>> End -}", + """ + class C + { + <<<<<<< Start + public void Goo(); + ||||||| Baseline + int removed; + ======= + public void Bar(); + >>>>>>> End + } + """, testHost, Keyword("class"), Class("C"), @@ -4484,9 +4788,11 @@ await TestAsync( [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_InsideMethod(TestHost testHost) { - await TestInMethodAsync(@" -var unmanaged = 0; -unmanaged++;", + await TestInMethodAsync(""" + + var unmanaged = 0; + unmanaged++; + """, testHost, Keyword("var"), Local("unmanaged"), @@ -4520,9 +4826,11 @@ await TestAsync( [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_Type_ExistingInterface(TestHost testHost) { - await TestAsync(@" -interface unmanaged {} -class X where T : unmanaged { }", + await TestAsync(""" + + interface unmanaged {} + class X where T : unmanaged { } + """, testHost, Keyword("interface"), Interface("unmanaged"), @@ -4544,12 +4852,14 @@ class X where T : unmanaged { }", [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_Type_ExistingInterfaceButOutOfScope(TestHost testHost) { - await TestAsync(@" -namespace OtherScope -{ - interface unmanaged {} -} -class X where T : unmanaged { }", + await TestAsync(""" + + namespace OtherScope + { + interface unmanaged {} + } + class X where T : unmanaged { } + """, testHost, Keyword("namespace"), Namespace("OtherScope"), @@ -4575,11 +4885,13 @@ class X where T : unmanaged { }", [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_Method_Keyword(TestHost testHost) { - await TestAsync(@" -class X -{ - void M() where T : unmanaged { } -}", + await TestAsync(""" + + class X + { + void M() where T : unmanaged { } + } + """, testHost, Keyword("class"), Class("X"), @@ -4603,12 +4915,14 @@ void M() where T : unmanaged { } [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_Method_ExistingInterface(TestHost testHost) { - await TestAsync(@" -interface unmanaged {} -class X -{ - void M() where T : unmanaged { } -}", + await TestAsync(""" + + interface unmanaged {} + class X + { + void M() where T : unmanaged { } + } + """, testHost, Keyword("interface"), Interface("unmanaged"), @@ -4636,15 +4950,17 @@ void M() where T : unmanaged { } [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_Method_ExistingInterfaceButOutOfScope(TestHost testHost) { - await TestAsync(@" -namespace OtherScope -{ - interface unmanaged {} -} -class X -{ - void M() where T : unmanaged { } -}", + await TestAsync(""" + + namespace OtherScope + { + interface unmanaged {} + } + class X + { + void M() where T : unmanaged { } + } + """, testHost, Keyword("namespace"), Namespace("OtherScope"), @@ -4697,9 +5013,11 @@ await TestAsync( [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_Delegate_ExistingInterface(TestHost testHost) { - await TestAsync(@" -interface unmanaged {} -delegate void D() where T : unmanaged;", + await TestAsync(""" + + interface unmanaged {} + delegate void D() where T : unmanaged; + """, testHost, Keyword("interface"), Interface("unmanaged"), @@ -4723,12 +5041,14 @@ interface unmanaged {} [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_Delegate_ExistingInterfaceButOutOfScope(TestHost testHost) { - await TestAsync(@" -namespace OtherScope -{ - interface unmanaged {} -} -delegate void D() where T : unmanaged;", + await TestAsync(""" + + namespace OtherScope + { + interface unmanaged {} + } + delegate void D() where T : unmanaged; + """, testHost, Keyword("namespace"), Namespace("OtherScope"), @@ -4756,14 +5076,16 @@ interface unmanaged {} [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_LocalFunction_Keyword(TestHost testHost) { - await TestAsync(@" -class X -{ - void N() - { - void M() where T : unmanaged { } - } -}", + await TestAsync(""" + + class X + { + void N() + { + void M() where T : unmanaged { } + } + } + """, testHost, Keyword("class"), Class("X"), @@ -4793,15 +5115,17 @@ void M() where T : unmanaged { } [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_LocalFunction_ExistingInterface(TestHost testHost) { - await TestAsync(@" -interface unmanaged {} -class X -{ - void N() - { - void M() where T : unmanaged { } - } -}", + await TestAsync(""" + + interface unmanaged {} + class X + { + void N() + { + void M() where T : unmanaged { } + } + } + """, testHost, Keyword("interface"), Interface("unmanaged"), @@ -4835,18 +5159,20 @@ void M() where T : unmanaged { } [Theory, CombinatorialData] public async Task TestUnmanagedConstraint_LocalFunction_ExistingInterfaceButOutOfScope(TestHost testHost) { - await TestAsync(@" -namespace OtherScope -{ - interface unmanaged {} -} -class X -{ - void N() - { - void M() where T : unmanaged { } - } -}", + await TestAsync(""" + + namespace OtherScope + { + interface unmanaged {} + } + class X + { + void N() + { + void M() where T : unmanaged { } + } + } + """, testHost, Keyword("namespace"), Namespace("OtherScope"), @@ -4884,12 +5210,14 @@ void M() where T : unmanaged { } [Theory, CombinatorialData] public async Task TestDeclarationIsPattern(TestHost testHost) { - await TestInMethodAsync(@" -object foo; + await TestInMethodAsync(""" -if (foo is Action action) -{ -}", + object foo; + + if (foo is Action action) + { + } + """, testHost, Keyword("object"), Local("foo"), @@ -4908,14 +5236,16 @@ await TestInMethodAsync(@" [Theory, CombinatorialData] public async Task TestDeclarationSwitchPattern(TestHost testHost) { - await TestInMethodAsync(@" -object y; + await TestInMethodAsync(""" -switch (y) -{ - case int x: - break; -}", + object y; + + switch (y) + { + case int x: + break; + } + """, testHost, Keyword("object"), Local("y"), @@ -4937,8 +5267,10 @@ await TestInMethodAsync(@" [Theory, CombinatorialData] public async Task TestDeclarationExpression(TestHost testHost) { - await TestInMethodAsync(@" -int (foo, bar) = (1, 2);", + await TestInMethodAsync(""" + + int (foo, bar) = (1, 2); + """, testHost, Keyword("int"), Punctuation.OpenParen, @@ -4959,14 +5291,16 @@ await TestInMethodAsync(@" [WorkItem("https://github.com/dotnet/roslyn/issues/18956")] public async Task TestListPattern(TestHost testHost) { - await TestInMethodAsync(@" -switch (new int[0]) -{ - case [1, 2]: - break; - case [1, .. var end]: - break; -}", + await TestInMethodAsync(""" + + switch (new int[0]) + { + case [1, 2]: + break; + case [1, .. var end]: + break; + } + """, testHost, ControlKeyword("switch"), Punctuation.OpenParen, @@ -5004,11 +5338,13 @@ await TestInMethodAsync(@" [WorkItem("https://github.com/dotnet/roslyn/issues/18956")] public async Task TestListPattern2(TestHost testHost) { - await TestInMethodAsync(@" -_ = x switch -{ - [var start, .. var end] => 1 -}", + await TestInMethodAsync(""" + + _ = x switch + { + [var start, .. var end] => 1 + } + """, testHost, Identifier("_"), Operators.Equals, @@ -5032,9 +5368,11 @@ await TestInMethodAsync(@" [WorkItem("https://github.com/dotnet/roslyn/issues/18956")] public async Task TestVarPattern(TestHost testHost) { - await TestInMethodAsync(@" -_ = 1 is var x; -", + await TestInMethodAsync(""" + + _ = 1 is var x; + + """, testHost, Identifier("_"), Operators.Equals, @@ -5048,8 +5386,10 @@ await TestInMethodAsync(@" [Theory, CombinatorialData] public async Task TestTupleTypeSyntax(TestHost testHost) { - await TestInClassAsync(@" -public (int a, int b) Get() => null;", + await TestInClassAsync(""" + + public (int a, int b) Get() => null; + """, testHost, Keyword("public"), Punctuation.OpenParen, @@ -5070,10 +5410,12 @@ await TestInClassAsync(@" [Theory, CombinatorialData] public async Task TestOutParameter(TestHost testHost) { - await TestInMethodAsync(@" -if (int.TryParse(""1"", out int x)) -{ -}", + await TestInMethodAsync(""" + + if (int.TryParse("1", out int x)) + { + } + """, testHost, ControlKeyword("if"), Punctuation.OpenParen, @@ -5081,7 +5423,9 @@ await TestInMethodAsync(@" Operators.Dot, Identifier("TryParse"), Punctuation.OpenParen, - String(@"""1"""), + String(""" + "1" + """), Punctuation.Comma, Keyword("out"), Keyword("int"), @@ -5095,9 +5439,11 @@ await TestInMethodAsync(@" [Theory, CombinatorialData] public async Task TestOutParameter2(TestHost testHost) { - await TestInClassAsync(@" -int F = int.TryParse(""1"", out int x) ? x : -1; -", + await TestInClassAsync(""" + + int F = int.TryParse("1", out int x) ? x : -1; + + """, testHost, Keyword("int"), Field("F"), @@ -5106,7 +5452,9 @@ await TestInClassAsync(@" Operators.Dot, Identifier("TryParse"), Punctuation.OpenParen, - String(@"""1"""), + String(""" + "1" + """), Punctuation.Comma, Keyword("out"), Keyword("int"), @@ -5187,9 +5535,11 @@ await TestAsync(code, [CombinatorialData] public async Task ForEachVariableStatement(TestHost testHost) { - await TestInMethodAsync(@" -foreach (var (x, y) in new[] { (1, 2) }); -", + await TestInMethodAsync(""" + + foreach (var (x, y) in new[] { (1, 2) }); + + """, testHost, ControlKeyword("foreach"), Punctuation.OpenParen, @@ -5217,9 +5567,11 @@ await TestInMethodAsync(@" [Theory, CombinatorialData] public async Task CatchDeclarationStatement(TestHost testHost) { - await TestInMethodAsync(@" -try { } catch (Exception ex) { } -", + await TestInMethodAsync(""" + + try { } catch (Exception ex) { } + + """, testHost, ControlKeyword("try"), Punctuation.OpenCurly, @@ -5236,9 +5588,11 @@ await TestInMethodAsync(@" [Theory, CombinatorialData] public async Task TestNotNullConstraint_InsideMethod(TestHost testHost) { - await TestInMethodAsync(@" -var notnull = 0; -notnull++;", + await TestInMethodAsync(""" + + var notnull = 0; + notnull++; + """, testHost, Keyword("var"), Local("notnull"), @@ -5272,9 +5626,11 @@ await TestAsync( [Theory, CombinatorialData] public async Task TestNotNullConstraint_Type_ExistingInterface(TestHost testHost) { - await TestAsync(@" -interface notnull {} -class X where T : notnull { }", + await TestAsync(""" + + interface notnull {} + class X where T : notnull { } + """, testHost, Keyword("interface"), Interface("notnull"), @@ -5296,12 +5652,14 @@ class X where T : notnull { }", [Theory, CombinatorialData] public async Task TestNotNullConstraint_Type_ExistingInterfaceButOutOfScope(TestHost testHost) { - await TestAsync(@" -namespace OtherScope -{ - interface notnull {} -} -class X where T : notnull { }", + await TestAsync(""" + + namespace OtherScope + { + interface notnull {} + } + class X where T : notnull { } + """, testHost, Keyword("namespace"), Namespace("OtherScope"), @@ -5327,11 +5685,13 @@ class X where T : notnull { }", [Theory, CombinatorialData] public async Task TestNotNullConstraint_Method_Keyword(TestHost testHost) { - await TestAsync(@" -class X -{ - void M() where T : notnull { } -}", + await TestAsync(""" + + class X + { + void M() where T : notnull { } + } + """, testHost, Keyword("class"), Class("X"), @@ -5355,12 +5715,14 @@ void M() where T : notnull { } [Theory, CombinatorialData] public async Task TestNotNullConstraint_Method_ExistingInterface(TestHost testHost) { - await TestAsync(@" -interface notnull {} -class X -{ - void M() where T : notnull { } -}", + await TestAsync(""" + + interface notnull {} + class X + { + void M() where T : notnull { } + } + """, testHost, Keyword("interface"), Interface("notnull"), @@ -5388,15 +5750,17 @@ void M() where T : notnull { } [Theory, CombinatorialData] public async Task TestNotNullConstraint_Method_ExistingInterfaceButOutOfScope(TestHost testHost) { - await TestAsync(@" -namespace OtherScope -{ - interface notnull {} -} -class X -{ - void M() where T : notnull { } -}", + await TestAsync(""" + + namespace OtherScope + { + interface notnull {} + } + class X + { + void M() where T : notnull { } + } + """, testHost, Keyword("namespace"), Namespace("OtherScope"), @@ -5449,9 +5813,11 @@ await TestAsync( [Theory, CombinatorialData] public async Task TestNotNullConstraint_Delegate_ExistingInterface(TestHost testHost) { - await TestAsync(@" -interface notnull {} -delegate void D() where T : notnull;", + await TestAsync(""" + + interface notnull {} + delegate void D() where T : notnull; + """, testHost, Keyword("interface"), Interface("notnull"), @@ -5475,12 +5841,14 @@ interface notnull {} [Theory, CombinatorialData] public async Task TestNotNullConstraint_Delegate_ExistingInterfaceButOutOfScope(TestHost testHost) { - await TestAsync(@" -namespace OtherScope -{ - interface notnull {} -} -delegate void D() where T : notnull;", + await TestAsync(""" + + namespace OtherScope + { + interface notnull {} + } + delegate void D() where T : notnull; + """, testHost, Keyword("namespace"), Namespace("OtherScope"), @@ -5508,14 +5876,16 @@ interface notnull {} [Theory, CombinatorialData] public async Task TestNotNullConstraint_LocalFunction_Keyword(TestHost testHost) { - await TestAsync(@" -class X -{ - void N() - { - void M() where T : notnull { } - } -}", + await TestAsync(""" + + class X + { + void N() + { + void M() where T : notnull { } + } + } + """, testHost, Keyword("class"), Class("X"), @@ -5545,15 +5915,17 @@ void M() where T : notnull { } [Theory, CombinatorialData] public async Task TestNotNullConstraint_LocalFunction_ExistingInterface(TestHost testHost) { - await TestAsync(@" -interface notnull {} -class X -{ - void N() - { - void M() where T : notnull { } - } -}", + await TestAsync(""" + + interface notnull {} + class X + { + void N() + { + void M() where T : notnull { } + } + } + """, testHost, Keyword("interface"), Interface("notnull"), @@ -5587,18 +5959,20 @@ void M() where T : notnull { } [Theory, CombinatorialData] public async Task TestNotNullConstraint_LocalFunction_ExistingInterfaceButOutOfScope(TestHost testHost) { - await TestAsync(@" -namespace OtherScope -{ - interface notnull {} -} -class X -{ - void N() - { - void M() where T : notnull { } - } -}", + await TestAsync(""" + + namespace OtherScope + { + interface notnull {} + } + class X + { + void N() + { + void M() where T : notnull { } + } + } + """, testHost, Keyword("namespace"), Namespace("OtherScope"), @@ -5637,11 +6011,13 @@ void M() where T : notnull { } [WorkItem("https://github.com/dotnet/roslyn/issues/45807")] public async Task FunctionPointer(TestHost testHost) { - var code = @" -class C -{ - delegate* unmanaged[Stdcall, SuppressGCTransition] x; -}"; + var code = """ + + class C + { + delegate* unmanaged[Stdcall, SuppressGCTransition] x; + } + """; await TestAsync(code, testHost, @@ -5695,9 +6071,11 @@ public async Task TestXmlAttributeNameSpan1() [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48094")] public async Task TestXmlAttributeNameSpan2() { - var source = @" -/// "; + var source = """ + + /// + """; using var workspace = CreateWorkspace(source, options: null, TestHost.InProcess); var document = workspace.CurrentSolution.Projects.First().Documents.First(); @@ -5725,14 +6103,16 @@ public async Task TestXmlAttributeNameSpan2() [CombinatorialData] public async Task TestStaticLocalFunction(TestHost testHost) { - var code = @" -class C -{ - public static void M() - { - static void LocalFunc() { } - } -}"; + var code = """ + + class C + { + public static void M() + { + static void LocalFunc() { } + } + } + """; await TestAsync(code, testHost, @@ -5763,14 +6143,16 @@ await TestAsync(code, [CombinatorialData] public async Task TestConstantLocalVariable(TestHost testHost) { - var code = @" -class C -{ - public static void M() - { - const int Zero = 0; - } -}"; + var code = """ + + class C + { + public static void M() + { + const int Zero = 0; + } + } + """; await TestAsync(code, testHost, @@ -5799,14 +6181,16 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task TestRawStringLiteral(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = """"""Hello world""""""; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = """Hello world"""; + } + } + """"; await TestAsync(code, testHost, @@ -5826,7 +6210,9 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String("\"\"\"Hello world\"\"\""), + String("""" + """Hello world""" + """"), Punctuation.Semicolon, Punctuation.CloseCurly, Punctuation.CloseCurly); @@ -5835,14 +6221,16 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task TestRawStringLiteralUtf8_01(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = """"""Hello world""""""u8; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = """Hello world"""u8; + } + } + """"; await TestAsync(code, testHost, @@ -5862,7 +6250,9 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String("\"\"\"Hello world\"\"\""), + String("""" + """Hello world""" + """"), Keyword("u8"), Punctuation.Semicolon, Punctuation.CloseCurly, @@ -5872,14 +6262,16 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task TestRawStringLiteralUtf8_02(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = """"""Hello world""""""U8; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = """Hello world"""U8; + } + } + """"; await TestAsync(code, testHost, @@ -5899,7 +6291,9 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String("\"\"\"Hello world\"\"\""), + String("""" + """Hello world""" + """"), Keyword("U8"), Punctuation.Semicolon, Punctuation.CloseCurly, @@ -5909,16 +6303,18 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task TestRawStringLiteralMultiline(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = """""" - Hello world - """"""; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = """ + Hello world + """; + } + } + """"; await TestAsync(code, testHost, @@ -5938,9 +6334,11 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String(@""""""" - Hello world - """""""), + String("""" + """ + Hello world + """ + """"), Punctuation.Semicolon, Punctuation.CloseCurly, Punctuation.CloseCurly); @@ -5949,16 +6347,18 @@ Hello world [Theory, CombinatorialData] public async Task TestRawStringLiteralMultilineUtf8_01(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = """""" - Hello world - """"""u8; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = """ + Hello world + """u8; + } + } + """"; await TestAsync(code, testHost, @@ -5978,9 +6378,11 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String(@""""""" - Hello world - """""""), + String("""" + """ + Hello world + """ + """"), Keyword("u8"), Punctuation.Semicolon, Punctuation.CloseCurly, @@ -5990,16 +6392,18 @@ Hello world [Theory, CombinatorialData] public async Task TestRawStringLiteralMultilineUtf8_02(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = """""" - Hello world - """"""U8; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = """ + Hello world + """U8; + } + } + """"; await TestAsync(code, testHost, @@ -6019,9 +6423,11 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String(@""""""" - Hello world - """""""), + String("""" + """ + Hello world + """ + """"), Keyword("U8"), Punctuation.Semicolon, Punctuation.CloseCurly, @@ -6031,14 +6437,16 @@ Hello world [Theory, CombinatorialData] public async Task TestRawStringLiteralInterpolation1(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = $""""""{x}""""""; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = $"""{x}"""; + } + } + """"; await TestAsync(code, testHost, @@ -6058,11 +6466,15 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String("$\"\"\""), + String("""" + $""" + """"), Punctuation.OpenCurly, Identifier("x"), Punctuation.CloseCurly, - String("\"\"\""), + String("""" + """ + """"), Punctuation.Semicolon, Punctuation.CloseCurly, Punctuation.CloseCurly); @@ -6071,14 +6483,16 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task TestRawStringLiteralInterpolation2(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = $$""""""{{x}}""""""; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = $$"""{{x}}"""; + } + } + """"; await TestAsync(code, testHost, @@ -6098,11 +6512,15 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String("$$\"\"\""), + String("""" + $$""" + """"), PunctuationText("{{"), Identifier("x"), PunctuationText("}}"), - String("\"\"\""), + String("""" + """ + """"), Punctuation.Semicolon, Punctuation.CloseCurly, Punctuation.CloseCurly); @@ -6111,14 +6529,16 @@ await TestAsync(code, [Theory, CombinatorialData] public async Task TestRawStringLiteralInterpolation3(TestHost testHost) { - var code = @" -class C -{ - public static void M(int x) - { - var s = $$""""""{{{x}}}""""""; - } -}"; + var code = """" + + class C + { + public static void M(int x) + { + var s = $$"""{{{x}}}"""; + } + } + """"; await TestAsync(code, testHost, @@ -6138,13 +6558,17 @@ await TestAsync(code, Keyword("var"), Local("s"), Operators.Equals, - String("$$\"\"\""), + String("""" + $$""" + """"), String("{"), PunctuationText("{{"), Identifier("x"), PunctuationText("}}"), String("}"), - String("\"\"\""), + String("""" + """ + """"), Punctuation.Semicolon, Punctuation.CloseCurly, Punctuation.CloseCurly); @@ -6154,10 +6578,12 @@ await TestAsync(code, public async Task CheckedUserDefinedOperators_01(TestHost testHost) { await TestInClassAsync( -@" -static T operator checked -(T a) -{ -}", + """ + + static T operator checked -(T a) + { + } + """, testHost, Keyword("static"), Identifier("T"), @@ -6176,10 +6602,12 @@ await TestInClassAsync( public async Task CheckedUserDefinedOperators_02(TestHost testHost) { await TestInClassAsync( -@" -static T operator checked +(T a, T b) -{ -}", + """ + + static T operator checked +(T a, T b) + { + } + """, testHost, Keyword("static"), Identifier("T"), @@ -6201,10 +6629,12 @@ await TestInClassAsync( public async Task CheckedUserDefinedOperators_03(TestHost testHost) { await TestInClassAsync( -@" -static explicit operator checked T(T a) -{ -}", + """ + + static explicit operator checked T(T a) + { + } + """, testHost, Keyword("static"), Keyword("explicit"), @@ -6223,10 +6653,12 @@ static explicit operator checked T(T a) public async Task CheckedUserDefinedOperators_04(TestHost testHost) { await TestInClassAsync( -@" -static T I1.operator checked -(T a) -{ -}", + """ + + static T I1.operator checked -(T a) + { + } + """, testHost, Keyword("static"), Identifier("T"), @@ -6247,10 +6679,12 @@ await TestInClassAsync( public async Task CheckedUserDefinedOperators_05(TestHost testHost) { await TestInClassAsync( -@" -static T I1.operator checked +(T a, T b) -{ -}", + """ + + static T I1.operator checked +(T a, T b) + { + } + """, testHost, Keyword("static"), Identifier("T"), @@ -6274,10 +6708,12 @@ await TestInClassAsync( public async Task CheckedUserDefinedOperators_06(TestHost testHost) { await TestInClassAsync( -@" -static explicit I1.operator checked T(T a) -{ -}", + """ + + static explicit I1.operator checked T(T a) + { + } + """, testHost, Keyword("static"), Keyword("explicit"), @@ -6298,10 +6734,12 @@ static explicit I1.operator checked T(T a) public async Task UnsignedRightShift_01(TestHost testHost) { await TestInClassAsync( -@" -static T operator >>>(T a, int b) -{ -}", + """ + + static T operator >>>(T a, int b) + { + } + """, testHost, Keyword("static"), Identifier("T"), @@ -6322,10 +6760,12 @@ await TestInClassAsync( public async Task UnsignedRightShift_02(TestHost testHost) { await TestInClassAsync( -@" -static T I1.operator checked >>>(T a, T b) -{ -}", + """ + + static T I1.operator checked >>>(T a, T b) + { + } + """, testHost, Keyword("static"), Identifier("T"), @@ -6349,12 +6789,14 @@ await TestInClassAsync( public async Task TestExclamationExclamation(TestHost testHost) { await TestAsync( -@"class C -{ - void M(string v!!) - { - } -}", + """ + class C + { + void M(string v!!) + { + } + } + """, testHost, Keyword("class"), Class("C"), @@ -6570,7 +7012,9 @@ void M() Keyword("string"), Parameter("s"), Operators.Equals, - String(@"""a string"""), + String(""" + "a string" + """), Punctuation.CloseParen, Operators.EqualsGreaterThan, Identifier("s"), diff --git a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests_Preprocessor.cs b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests_Preprocessor.cs index e2ebf43e96a5c..5c944e8a456c0 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests_Preprocessor.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests_Preprocessor.cs @@ -1282,6 +1282,33 @@ await TestAsync(code, Number("102")); } + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public async Task PP_AfterNonWhiteSpaceOnLine(TestHost testHost) + { + var code = """ + if (#if false + true + #else + false + #endif + ) { } + """; + + await TestAsync(code, + testHost, + ControlKeyword("if"), + Punctuation.OpenParen, + Keyword("true"), + PPKeyword("#"), + PPKeyword("else"), + Keyword("false"), + PPKeyword("#"), + PPKeyword("endif"), + Punctuation.CloseParen, + Punctuation.OpenCurly, + Punctuation.CloseCurly); + } + [Theory, CombinatorialData] public async Task DiscardInOutDeclaration(TestHost testHost) { diff --git a/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs index 7254e15840547..1b977d47ab288 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs @@ -3969,7 +3969,6 @@ class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } diff --git a/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs b/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs index 2da0b9dcaf527..8a6acfa346e29 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs @@ -2271,6 +2271,104 @@ public struct Bar isNewFile: false, assertGenerateTypeDialogOptions: new GenerateTypeDialogOptions(false, TypeKindOptions.Class | TypeKindOptions.Structure, false)); } + + [Fact, WorkItem(63280, "https://github.com/dotnet/roslyn/issues/63280")] + public async Task GenerateType_GenericBaseList() + { + await TestWithMockedGenerateTypeDialog( +initial: @" +using System.Collections.Generic; + +struct C : IEnumerable<[|$$NewType|]> +{ +}", +languageName: LanguageNames.CSharp, +typeName: "NewType", +expected: @" +using System.Collections.Generic; + +struct C : IEnumerable +{ +} + +public class NewType +{ +}", +accessibility: Accessibility.Public, +typeKind: TypeKind.Class, +isNewFile: false, +assertGenerateTypeDialogOptions: new GenerateTypeDialogOptions(false, TypeKindOptions.AllOptions, false)); + } + + [Fact] + public async Task GenerateType_QualifiedBaseList() + { + await TestWithMockedGenerateTypeDialog( +initial: @" +using System.Collections.Generic; + +struct C : A.B.[|$$INewType|] +{ +} + +namespace A.B +{ +}", +languageName: LanguageNames.CSharp, +typeName: "INewType", +expected: @" +using System.Collections.Generic; + +struct C : A.B.INewType +{ +} + +namespace A.B +{ + public interface INewType + { + } +}", +accessibility: Accessibility.Public, +typeKind: TypeKind.Interface, +isNewFile: false, +assertGenerateTypeDialogOptions: new GenerateTypeDialogOptions(false, TypeKindOptions.Interface, false)); + } + + [Fact] + public async Task GenerateType_AliasQualifiedBaseList() + { + await TestWithMockedGenerateTypeDialog( +initial: @" +using System.Collections.Generic; + +struct C : global::A.B.[|$$INewType|] +{ +} + +namespace A.B +{ +}", +languageName: LanguageNames.CSharp, +typeName: "INewType", +expected: @" +using System.Collections.Generic; + +struct C : global::A.B.INewType +{ +} + +namespace A.B +{ + public interface INewType + { + } +}", +accessibility: Accessibility.Public, +typeKind: TypeKind.Interface, +isNewFile: false, +assertGenerateTypeDialogOptions: new GenerateTypeDialogOptions(false, TypeKindOptions.Interface, false)); + } #endregion #region Delegates [Fact] diff --git a/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/AddParameterCheckTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/AddParameterCheckTests.cs index cf878d73011f8..51e66334cc26d 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/AddParameterCheckTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/AddParameterCheckTests.cs @@ -49,8 +49,7 @@ public C([||]string s) await new VerifyCS.Test { LanguageVersion = LanguageVersion.CSharp11, - TestCode = testCode, - FixedCode = testCode + TestCode = testCode }.RunAsync(); } @@ -261,8 +260,7 @@ partial void M(string s) await new VerifyCS.Test { LanguageVersion = languageVersion, - TestCode = code, - FixedCode = code + TestCode = code }.RunAsync(); } @@ -284,8 +282,7 @@ public partial void M(string s) await new VerifyCS.Test { LanguageVersion = LanguageVersion.CSharp9, - TestCode = code, - FixedCode = code + TestCode = code }.RunAsync(); } @@ -309,8 +306,7 @@ partial void M(string s) await new VerifyCS.Test { LanguageVersion = languageVersion, - TestCode = code, - FixedCode = code + TestCode = code }.RunAsync(); } @@ -332,8 +328,7 @@ public partial void M(string s) await new VerifyCS.Test { LanguageVersion = LanguageVersion.CSharp9, - TestCode = code, - FixedCode = code + TestCode = code }.RunAsync(); } @@ -1717,8 +1712,7 @@ public C() await new VerifyCS.Test { LanguageVersion = LanguageVersion.CSharp11, - TestCode = testCode, - FixedCode = testCode + TestCode = testCode }.RunAsync(); } @@ -1831,7 +1825,6 @@ int this[[||]string s] await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -2933,7 +2926,6 @@ record C([||]string s) { public string s; } { LanguageVersion = version, TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -2947,7 +2939,6 @@ class C([||]string s) { public string s; } { LanguageVersion = LanguageVersion.CSharp12, TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -2961,7 +2952,6 @@ struct C([||]string s) { public string s; } { LanguageVersion = LanguageVersion.CSharp12, TestCode = code, - FixedCode = code, }.RunAsync(); } diff --git a/src/EditorFeatures/CSharpTest/CodeActions/MoveStaticMembers/CSharpMoveStaticMembersTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/MoveStaticMembers/CSharpMoveStaticMembersTests.cs index 62a5bf040e58a..356a59d1b690b 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/MoveStaticMembers/CSharpMoveStaticMembersTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/MoveStaticMembers/CSharpMoveStaticMembersTests.cs @@ -3224,7 +3224,6 @@ public class Class1 await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, }.RunAsync().ConfigureAwait(false); } @@ -3242,7 +3241,6 @@ public class Class1 await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, }.RunAsync().ConfigureAwait(false); } @@ -3260,7 +3258,6 @@ public class Class1 await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, }.RunAsync().ConfigureAwait(false); } @@ -3278,7 +3275,6 @@ public class Class1 await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, }.RunAsync().ConfigureAwait(false); } @@ -3296,7 +3292,6 @@ public class Class1 await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, }.RunAsync().ConfigureAwait(false); } @@ -3389,7 +3384,6 @@ public async Task TestSelectTopLevelStatement_NoAction1() await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp10, TestState = { @@ -3410,7 +3404,6 @@ public async Task TestSelectTopLevelStatement_NoAction2() await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp10, TestState = { @@ -3434,7 +3427,6 @@ public async Task TestSelectTopLevelLocalFunction_NoAction() await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp10, TestState = { @@ -3557,7 +3549,6 @@ private static async Task TestNoRefactoringAsync(string initialMarkup) await new Test("", ImmutableArray.Empty, "") { TestCode = initialMarkup, - FixedCode = initialMarkup, }.RunAsync().ConfigureAwait(false); } } diff --git a/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs b/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs index 8461a149443d3..12cfecdc623d2 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Formatting; @@ -14,13 +12,13 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeActions.MoveType; [Trait(Traits.Feature, Traits.Features.CodeActionsMoveType)] -public partial class MoveTypeTests : CSharpMoveTypeTestsBase +public sealed partial class MoveTypeTests : CSharpMoveTypeTestsBase { [WpfFact] public async Task TestMissing_OnMatchingFileName() { var code = -@"[||]class test1 { }"; + @"[||]class test1 { }"; await TestMissingInRegularAndScriptAsync(code); } @@ -29,10 +27,12 @@ public async Task TestMissing_OnMatchingFileName() public async Task TestMissing_Nested_OnMatchingFileName_Simple() { var code = -@"class outer -{ - [||]class test1 { } -}"; + """ + class outer + { + [||]class test1 { } + } + """; await TestMissingInRegularAndScriptAsync(code); } @@ -41,7 +41,7 @@ public async Task TestMissing_Nested_OnMatchingFileName_Simple() public async Task TestMatchingFileName_CaseSensitive() { var code = -@"[||]class Test1 { }"; + @"[||]class Test1 { }"; await TestActionCountAsync(code, count: 2); } @@ -50,8 +50,10 @@ public async Task TestMatchingFileName_CaseSensitive() public async Task TestForSpans1() { var code = -@"[|clas|]s Class1 { } - class Class2 { }"; + """ + [|clas|]s Class1 { } + class Class2 { } + """; await TestActionCountAsync(code, count: 3); } @@ -60,13 +62,17 @@ class Class2 { }"; public async Task TestForSpans2() { var code = -@"[||]class Class1 { } - class Class2 { }"; + """ + [||]class Class1 { } + class Class2 { } + """; var codeAfterMove = @"class Class2 { }"; var expectedDocumentName = "Class1.cs"; - var destinationDocumentText = @"class Class1 { } -"; + var destinationDocumentText = """ + class Class1 { } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -75,21 +81,27 @@ class Class2 { }"; public async Task TestMoveToNewFileWithFolders() { var code = -@" - - - -[||]class Class1 { } -class Class2 { } - - -"; - var codeAfterMove = @"class Class2 { } - "; + """ + + + + + [||]class Class1 { } + class Class2 { } + + + + """; + var codeAfterMove = """ + class Class2 { } + + """; var expectedDocumentName = "Class1.cs"; - var destinationDocumentText = @"class Class1 { } - "; + var destinationDocumentText = """ + class Class1 { } + + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, @@ -100,8 +112,10 @@ await TestMoveTypeToNewFileAsync( public async Task TestForSpans3() { var code = -@"[|class Class1|] { } -class Class2 { }"; + """ + [|class Class1|] { } + class Class2 { } + """; await TestActionCountAsync(code, count: 3); } @@ -110,13 +124,17 @@ class Class2 { }"; public async Task TestForSpans4() { var code = -@"class Class1[||] { } -class Class2 { }"; + """ + class Class1[||] { } + class Class2 { } + """; var codeAfterMove = @"class Class2 { }"; var expectedDocumentName = "Class1.cs"; - var destinationDocumentText = @"class Class1 { } -"; + var destinationDocumentText = """ + class Class1 { } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -125,13 +143,17 @@ class Class2 { }"; public async Task MoveTypeWithNoContainerNamespace() { var code = -@"[||]class Class1 { } -class Class2 { }"; + """ + [||]class Class1 { } + class Class2 { } + """; var codeAfterMove = @"class Class2 { }"; var expectedDocumentName = "Class1.cs"; - var destinationDocumentText = @"class Class1 { } -"; + var destinationDocumentText = """ + class Class1 { } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -140,22 +162,28 @@ class Class2 { }"; public async Task MoveTypeWithWithUsingsAndNoContainerNamespace() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; -[||]class Class1 { } -class Class2 { }"; + [||]class Class1 { } + class Class2 { } + """; var codeAfterMove = -@"// Banner Text -using System; -class Class2 { }"; + """ + // Banner Text + using System; + class Class2 { } + """; var expectedDocumentName = "Class1.cs"; var destinationDocumentText = -@"// Banner Text -class Class1 { } -"; + """ + // Banner Text + class Class1 { } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -164,35 +192,41 @@ class Class1 { } public async Task MoveTypeWithWithMembers() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; -[||]class Class1 -{ - void Print(int x) - { - Console.WriteLine(x); - } -} -class Class2 { }"; + [||]class Class1 + { + void Print(int x) + { + Console.WriteLine(x); + } + } + class Class2 { } + """; var codeAfterMove = -@"// Banner Text -class Class2 { }"; + """ + // Banner Text + class Class2 { } + """; var expectedDocumentName = "Class1.cs"; var destinationDocumentText = -@"// Banner Text -using System; + """ + // Banner Text + using System; -class Class1 -{ - void Print(int x) - { - Console.WriteLine(x); - } -} -"; + class Class1 + { + void Print(int x) + { + Console.WriteLine(x); + } + } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -201,50 +235,56 @@ void Print(int x) public async Task MoveTypeWithWithMembers2() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; -[||]class Class1 -{ - void Print(int x) - { - Console.WriteLine(x); - } -} + [||]class Class1 + { + void Print(int x) + { + Console.WriteLine(x); + } + } -class Class2 -{ - void Print(int x) - { - Console.WriteLine(x); - } -}"; + class Class2 + { + void Print(int x) + { + Console.WriteLine(x); + } + } + """; var codeAfterMove = -@"// Banner Text -using System; + """ + // Banner Text + using System; -class Class2 -{ - void Print(int x) - { - Console.WriteLine(x); - } -}"; + class Class2 + { + void Print(int x) + { + Console.WriteLine(x); + } + } + """; var expectedDocumentName = "Class1.cs"; var destinationDocumentText = -@"// Banner Text -using System; + """ + // Banner Text + using System; -class Class1 -{ - void Print(int x) - { - Console.WriteLine(x); - } -} -"; + class Class1 + { + void Print(int x) + { + Console.WriteLine(x); + } + } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -253,13 +293,17 @@ void Print(int x) public async Task MoveAnInterface() { var code = -@"[||]interface IMoveType { } -class Class2 { }"; + """ + [||]interface IMoveType { } + class Class2 { } + """; var codeAfterMove = @"class Class2 { }"; var expectedDocumentName = "IMoveType.cs"; - var destinationDocumentText = @"interface IMoveType { } -"; + var destinationDocumentText = """ + interface IMoveType { } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -268,13 +312,17 @@ class Class2 { }"; public async Task MoveAStruct() { var code = -@"[||]struct MyStruct { } -class Class2 { }"; + """ + [||]struct MyStruct { } + class Class2 { } + """; var codeAfterMove = @"class Class2 { }"; var expectedDocumentName = "MyStruct.cs"; - var destinationDocumentText = @"struct MyStruct { } -"; + var destinationDocumentText = """ + struct MyStruct { } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -283,13 +331,17 @@ class Class2 { }"; public async Task MoveAnEnum() { var code = -@"[||]enum MyEnum { } -class Class2 { }"; + """ + [||]enum MyEnum { } + class Class2 { } + """; var codeAfterMove = @"class Class2 { }"; var expectedDocumentName = "MyEnum.cs"; - var destinationDocumentText = @"enum MyEnum { } -"; + var destinationDocumentText = """ + enum MyEnum { } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -298,25 +350,31 @@ class Class2 { }"; public async Task MoveTypeWithWithContainerNamespace() { var code = -@"namespace N1 -{ - [||]class Class1 { } - class Class2 { } -}"; + """ + namespace N1 + { + [||]class Class1 { } + class Class2 { } + } + """; var codeAfterMove = -@"namespace N1 -{ - class Class2 { } -}"; + """ + namespace N1 + { + class Class2 { } + } + """; var expectedDocumentName = "Class1.cs"; var destinationDocumentText = -@"namespace N1 -{ - class Class1 { } -}"; + """ + namespace N1 + { + class Class1 { } + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -324,24 +382,30 @@ class Class1 { } public async Task MoveTypeWithWithFileScopedNamespace() { var code = -@"namespace N1; + """ + namespace N1; -[||]class Class1 { } -class Class2 { } -"; + [||]class Class1 { } + class Class2 { } + + """; var codeAfterMove = -@"namespace N1; -class Class2 { } -"; + """ + namespace N1; + class Class2 { } + + """; var expectedDocumentName = "Class1.cs"; var destinationDocumentText = -@"namespace N1; + """ + namespace N1; -class Class1 { } -"; + class Class1 { } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -349,35 +413,41 @@ class Class1 { } public async Task MoveNestedTypeToNewFile_Simple() { var code = -@"namespace N1 -{ - class Class1 - { - [||]class Class2 { } - } - -}"; + """ + namespace N1 + { + class Class1 + { + [||]class Class2 { } + } + + } + """; var codeAfterMove = -@"namespace N1 -{ - partial class Class1 - { - } - -}"; + """ + namespace N1 + { + partial class Class1 + { + } + + } + """; var expectedDocumentName = "Class2.cs"; var destinationDocumentText = -@"namespace N1 -{ - partial class Class1 - { - class Class2 { } - } - -}"; + """ + namespace N1 + { + partial class Class1 + { + class Class2 { } + } + + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -385,35 +455,41 @@ class Class2 { } public async Task MoveNestedTypePreserveModifiers() { var code = -@"namespace N1 -{ - abstract class Class1 - { - [||]class Class2 { } - } - -}"; + """ + namespace N1 + { + abstract class Class1 + { + [||]class Class2 { } + } + + } + """; var codeAfterMove = -@"namespace N1 -{ - abstract partial class Class1 - { - } - -}"; + """ + namespace N1 + { + abstract partial class Class1 + { + } + + } + """; var expectedDocumentName = "Class2.cs"; var destinationDocumentText = -@"namespace N1 -{ - abstract partial class Class1 - { - class Class2 { } - } - -}"; + """ + namespace N1 + { + abstract partial class Class1 + { + class Class2 { } + } + + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -421,39 +497,45 @@ class Class2 { } public async Task MoveNestedTypeToNewFile_Attributes1() { var code = -@"namespace N1 -{ - [Outer] - class Class1 - { - [Inner] - [||]class Class2 { } - } - -}"; + """ + namespace N1 + { + [Outer] + class Class1 + { + [Inner] + [||]class Class2 { } + } + + } + """; var codeAfterMove = -@"namespace N1 -{ - [Outer] - partial class Class1 - { - } - -}"; + """ + namespace N1 + { + [Outer] + partial class Class1 + { + } + + } + """; var expectedDocumentName = "Class2.cs"; var destinationDocumentText = -@"namespace N1 -{ - partial class Class1 - { - [Inner] - class Class2 { } - } - -}"; + """ + namespace N1 + { + partial class Class1 + { + [Inner] + class Class2 { } + } + + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -461,40 +543,46 @@ class Class2 { } public async Task MoveNestedTypeToNewFile_Comments1() { var code = -@"namespace N1 -{ - /// Outer doc comment. - class Class1 - { - /// Inner doc comment - [||]class Class2 - { - } - } -}"; + """ + namespace N1 + { + /// Outer doc comment. + class Class1 + { + /// Inner doc comment + [||]class Class2 + { + } + } + } + """; var codeAfterMove = -@"namespace N1 -{ - /// Outer doc comment. - partial class Class1 - { - } -}"; + """ + namespace N1 + { + /// Outer doc comment. + partial class Class1 + { + } + } + """; var expectedDocumentName = "Class2.cs"; var destinationDocumentText = -@"namespace N1 -{ - partial class Class1 - { - /// Inner doc comment - class Class2 - { - } - } -}"; + """ + namespace N1 + { + partial class Class1 + { + /// Inner doc comment + class Class2 + { + } + } + } + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -503,35 +591,41 @@ await TestMoveTypeToNewFileAsync( public async Task MoveNestedTypeToNewFile_Simple_DottedName() { var code = -@"namespace N1 -{ - class Class1 - { - [||]class Class2 { } - } - -}"; + """ + namespace N1 + { + class Class1 + { + [||]class Class2 { } + } + + } + """; var codeAfterMove = -@"namespace N1 -{ - partial class Class1 - { - } - -}"; + """ + namespace N1 + { + partial class Class1 + { + } + + } + """; var expectedDocumentName = "Class1.Class2.cs"; var destinationDocumentText = -@"namespace N1 -{ - partial class Class1 - { - class Class2 { } - } - -}"; + """ + namespace N1 + { + partial class Class1 + { + class Class2 { } + } + + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText, index: 1); } @@ -539,27 +633,33 @@ class Class2 { } public async Task MoveNestedTypeToNewFile_Simple_DottedName_WithPrimaryConstructor() { var code = -@"internal class Outer() -{ - private class Inner[||] - { - } -}"; + """ + internal class Outer() + { + private class Inner[||] + { + } + } + """; var codeAfterMove = -@"internal partial class Outer() -{ -}"; + """ + internal partial class Outer() + { + } + """; var expectedDocumentName = "Outer.Inner.cs"; var destinationDocumentText = -@"internal partial class Outer -{ - private class Inner - { - } -}"; + """ + internal partial class Outer + { + private class Inner + { + } + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText, index: 1); } @@ -567,42 +667,48 @@ private class Inner public async Task MoveNestedTypeToNewFile_ParentHasOtherMembers() { var code = -@"namespace N1 -{ - class Class1 - { - private int _field1; + """ + namespace N1 + { + class Class1 + { + private int _field1; - [||]class Class2 { } + [||]class Class2 { } - public void Method1() { } - } - -}"; + public void Method1() { } + } + + } + """; var codeAfterMove = -@"namespace N1 -{ - partial class Class1 - { - private int _field1; + """ + namespace N1 + { + partial class Class1 + { + private int _field1; - public void Method1() { } - } - -}"; + public void Method1() { } + } + + } + """; var expectedDocumentName = "Class2.cs"; var destinationDocumentText = -@"namespace N1 -{ - partial class Class1 - { - class Class2 { } - } - -}"; + """ + namespace N1 + { + partial class Class1 + { + class Class2 { } + } + + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -610,49 +716,55 @@ class Class2 { } public async Task MoveNestedTypeToNewFile_HasOtherTopLevelMembers() { var code = -@"namespace N1 -{ - class Class1 - { - private int _field1; + """ + namespace N1 + { + class Class1 + { + private int _field1; - [||]class Class2 { } + [||]class Class2 { } - public void Method1() { } - } + public void Method1() { } + } - internal class Class3 - { - private void Method1() { } - } -}"; + internal class Class3 + { + private void Method1() { } + } + } + """; var codeAfterMove = -@"namespace N1 -{ - partial class Class1 - { - private int _field1; + """ + namespace N1 + { + partial class Class1 + { + private int _field1; - public void Method1() { } - } + public void Method1() { } + } - internal class Class3 - { - private void Method1() { } - } -}"; + internal class Class3 + { + private void Method1() { } + } + } + """; var expectedDocumentName = "Class2.cs"; var destinationDocumentText = -@"namespace N1 -{ - partial class Class1 - { - class Class2 { } - } -}"; + """ + namespace N1 + { + partial class Class1 + { + class Class2 { } + } + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -660,161 +772,173 @@ class Class2 { } public async Task MoveNestedTypeToNewFile_HasMembers() { var code = -@"namespace N1 -{ - class Class1 - { - private int _field1; + """ + namespace N1 + { + class Class1 + { + private int _field1; - [||]class Class2 - { - private string _field1; - public void InnerMethod() { } - } + [||]class Class2 + { + private string _field1; + public void InnerMethod() { } + } - public void Method1() { } - } -}"; + public void Method1() { } + } + } + """; var codeAfterMove = -@"namespace N1 -{ - partial class Class1 - { - private int _field1; + """ + namespace N1 + { + partial class Class1 + { + private int _field1; - public void Method1() { } - } -}"; + public void Method1() { } + } + } + """; var expectedDocumentName = "Class2.cs"; var destinationDocumentText = -@"namespace N1 -{ - partial class Class1 - { - class Class2 - { - private string _field1; - public void InnerMethod() { } - } - } -}"; - await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); - } + """ + namespace N1 + { + partial class Class1 + { + class Class2 + { + private string _field1; + public void InnerMethod() { } + } + } + } + """; + await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); + } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/13969")] public async Task MoveTypeInFileWithComplexHierarchy() { var code = -@"namespace OuterN1.N1 -{ - namespace InnerN2.N2 - { - class OuterClass1 - { - class InnerClass2 + """ + namespace OuterN1.N1 { - } - } - } + namespace InnerN2.N2 + { + class OuterClass1 + { + class InnerClass2 + { + } + } + } - namespace InnerN3.N3 - { - class OuterClass2 - { - [||]class InnerClass2 - { - class InnerClass3 + namespace InnerN3.N3 { + class OuterClass2 + { + [||]class InnerClass2 + { + class InnerClass3 + { + } + } + + class InnerClass4 + { + } + } + + class OuterClass3 + { + } } } - class InnerClass4 + namespace OuterN2.N2 { + namespace InnerN3.N3 + { + class OuterClass5 { + class InnerClass6 { + } + } + } } - } - class OuterClass3 - { - } - } -} - -namespace OuterN2.N2 -{ - namespace InnerN3.N3 - { - class OuterClass5 { - class InnerClass6 { - } - } - } -} -"; + """; var codeAfterMove = -@"namespace OuterN1.N1 -{ - namespace InnerN2.N2 - { - class OuterClass1 - { - class InnerClass2 + """ + namespace OuterN1.N1 { - } - } - } + namespace InnerN2.N2 + { + class OuterClass1 + { + class InnerClass2 + { + } + } + } - namespace InnerN3.N3 - { - partial class OuterClass2 - { + namespace InnerN3.N3 + { + partial class OuterClass2 + { - class InnerClass4 - { - } - } + class InnerClass4 + { + } + } - class OuterClass3 - { - } - } -} + class OuterClass3 + { + } + } + } -namespace OuterN2.N2 -{ - namespace InnerN3.N3 - { - class OuterClass5 { - class InnerClass6 { + namespace OuterN2.N2 + { + namespace InnerN3.N3 + { + class OuterClass5 { + class InnerClass6 { + } + } + } } - } - } -} -"; + + """; var expectedDocumentName = "InnerClass2.cs"; var destinationDocumentText = -@"namespace OuterN1.N1 -{ - - namespace InnerN3.N3 - { - partial class OuterClass2 - { - class InnerClass2 + """ + namespace OuterN1.N1 { - class InnerClass3 + + namespace InnerN3.N3 { + partial class OuterClass2 + { + class InnerClass2 + { + class InnerClass3 + { + } + } + } } } - } - } -} -"; + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -822,40 +946,46 @@ class InnerClass3 public async Task MoveTypeUsings1() { var code = -@" -// Only used by inner type. -using System; + """ -// Unused by both types. -using System.Collections; + // Only used by inner type. + using System; -class Outer { - [||]class Inner { - DateTime d; - } -}"; - var codeAfterMove = @" -// Only used by inner type. + // Unused by both types. + using System.Collections; + + class Outer { + [||]class Inner { + DateTime d; + } + } + """; + var codeAfterMove = """ -// Unused by both types. -using System.Collections; + // Only used by inner type. -partial class Outer { -}"; + // Unused by both types. + using System.Collections; + + partial class Outer { + } + """; var expectedDocumentName = "Inner.cs"; var destinationDocumentText = -@" -// Only used by inner type. -using System; + """ -// Unused by both types. + // Only used by inner type. + using System; -partial class Outer { - class Inner { - DateTime d; - } -}"; + // Unused by both types. + + partial class Outer { + class Inner { + DateTime d; + } + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -864,33 +994,39 @@ class Inner { public async Task TestLeadingTrivia1() { var code = -@" -class Outer -{ - class Inner1 - { - } + """ - [||]class Inner2 - { - } -}"; - var codeAfterMove = @" -partial class Outer -{ - class Inner1 - { - } -}"; + class Outer + { + class Inner1 + { + } + + [||]class Inner2 + { + } + } + """; + var codeAfterMove = """ + + partial class Outer + { + class Inner1 + { + } + } + """; var expectedDocumentName = "Inner2.cs"; - var destinationDocumentText = @" -partial class Outer -{ - class Inner2 - { - } -}"; + var destinationDocumentText = """ + + partial class Outer + { + class Inner2 + { + } + } + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -900,34 +1036,40 @@ await TestMoveTypeToNewFileAsync( public async Task TestInsertFinalNewLine() { var code = -@" -class Outer -{ - class Inner1 - { - } + """ - [||]class Inner2 - { - } -}"; - var codeAfterMove = @" -partial class Outer -{ - class Inner1 - { - } -}"; + class Outer + { + class Inner1 + { + } + + [||]class Inner2 + { + } + } + """; + var codeAfterMove = """ + + partial class Outer + { + class Inner1 + { + } + } + """; var expectedDocumentName = "Inner2.cs"; - var destinationDocumentText = @" -partial class Outer -{ - class Inner2 - { - } -} -"; + var destinationDocumentText = """ + + partial class Outer + { + class Inner2 + { + } + } + + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText, @@ -938,33 +1080,39 @@ await TestMoveTypeToNewFileAsync( public async Task TestInsertFinalNewLine2() { var code = -@" -class Outer -{ - class Inner1 - { - } + """ - [||]class Inner2 - { - } -}"; - var codeAfterMove = @" -partial class Outer -{ - class Inner1 - { - } -}"; + class Outer + { + class Inner1 + { + } + + [||]class Inner2 + { + } + } + """; + var codeAfterMove = """ + + partial class Outer + { + class Inner1 + { + } + } + """; var expectedDocumentName = "Inner2.cs"; - var destinationDocumentText = @" -partial class Outer -{ - class Inner2 - { - } -}"; + var destinationDocumentText = """ + + partial class Outer + { + class Inner2 + { + } + } + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText, @@ -975,26 +1123,32 @@ await TestMoveTypeToNewFileAsync( public async Task MoveTypeRemoveOuterInheritanceTypes() { var code = -@" -class Outer : IComparable { - [||]class Inner : IWhatever { - DateTime d; - } -}"; + """ + + class Outer : IComparable { + [||]class Inner : IWhatever { + DateTime d; + } + } + """; var codeAfterMove = -@" -partial class Outer : IComparable { -}"; + """ + + partial class Outer : IComparable { + } + """; var expectedDocumentName = "Inner.cs"; var destinationDocumentText = -@" -partial class Outer -{ - class Inner : IWhatever { - DateTime d; - } -}"; + """ + + partial class Outer + { + class Inner : IWhatever { + DateTime d; + } + } + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1004,49 +1158,55 @@ await TestMoveTypeToNewFileAsync( public async Task MoveTypeWithDirectives1() { var code = -@"using System; + """ + using System; -namespace N -{ - class Program - { - static void Main() - { - } - } -} + namespace N + { + class Program + { + static void Main() + { + } + } + } -#if true -public class [||]Inner -{ + #if true + public class [||]Inner + { -} -#endif"; + } + #endif + """; var codeAfterMove = - @"using System; + """ + using System; -namespace N -{ - class Program - { - static void Main() + namespace N { + class Program + { + static void Main() + { + } + } } - } -} -#if true -#endif"; + #if true + #endif + """; var expectedDocumentName = "Inner.cs"; var destinationDocumentText = -@" -#if true -public class Inner -{ + """ -} -#endif"; + #if true + public class Inner + { + + } + #endif + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1056,54 +1216,60 @@ await TestMoveTypeToNewFileAsync( public async Task MoveTypeWithDirectives2() { var code = -@"using System; + """ + using System; -namespace N -{ - class Program - { - static void Main() - { - } + namespace N + { + class Program + { + static void Main() + { + } -#if true - public class [||]Inner - { + #if true + public class [||]Inner + { - } -#endif - } -}"; + } + #endif + } + } + """; var codeAfterMove = - @"using System; + """ + using System; -namespace N -{ - partial class Program - { - static void Main() + namespace N { - } + partial class Program + { + static void Main() + { + } -#if true -#endif - } -}"; + #if true + #endif + } + } + """; var expectedDocumentName = "Inner.cs"; var destinationDocumentText = -@"namespace N -{ - partial class Program - { -#if true - public class Inner - { + """ + namespace N + { + partial class Program + { + #if true + public class Inner + { - } -#endif - } -}"; + } + #endif + } + } + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1113,49 +1279,55 @@ await TestMoveTypeToNewFileAsync( public async Task TestLeadingBlankLines1() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; -[||]class Class1 -{ - void Foo() - { - Console.WriteLine(); - } -} + [||]class Class1 + { + void Foo() + { + Console.WriteLine(); + } + } -class Class2 -{ - void Foo() - { - Console.WriteLine(); - } -} -"; - var codeAfterMove = @"// Banner Text -using System; + class Class2 + { + void Foo() + { + Console.WriteLine(); + } + } -class Class2 -{ - void Foo() - { - Console.WriteLine(); - } -} -"; + """; + var codeAfterMove = """ + // Banner Text + using System; + + class Class2 + { + void Foo() + { + Console.WriteLine(); + } + } + + """; var expectedDocumentName = "Class1.cs"; - var destinationDocumentText = @"// Banner Text -using System; + var destinationDocumentText = """ + // Banner Text + using System; -class Class1 -{ - void Foo() - { - Console.WriteLine(); - } -} -"; + class Class1 + { + void Foo() + { + Console.WriteLine(); + } + } + + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1165,49 +1337,55 @@ await TestMoveTypeToNewFileAsync( public async Task TestLeadingBlankLines2() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; -class Class1 -{ - void Foo() - { - Console.WriteLine(); - } -} + class Class1 + { + void Foo() + { + Console.WriteLine(); + } + } -[||]class Class2 -{ - void Foo() - { - Console.WriteLine(); - } -} -"; - var codeAfterMove = @"// Banner Text -using System; + [||]class Class2 + { + void Foo() + { + Console.WriteLine(); + } + } -class Class1 -{ - void Foo() - { - Console.WriteLine(); - } -} -"; + """; + var codeAfterMove = """ + // Banner Text + using System; + + class Class1 + { + void Foo() + { + Console.WriteLine(); + } + } + + """; var expectedDocumentName = "Class2.cs"; - var destinationDocumentText = @"// Banner Text -using System; + var destinationDocumentText = """ + // Banner Text + using System; -class Class2 -{ - void Foo() - { - Console.WriteLine(); - } -} -"; + class Class2 + { + void Foo() + { + Console.WriteLine(); + } + } + + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1217,49 +1395,55 @@ await TestMoveTypeToNewFileAsync( public async Task TestLeadingCommentInContainer() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; + + class Class1 + // Leading comment + { + class [||]Class2 + { + } -class Class1 -// Leading comment -{ - class [||]Class2 - { - } + void Foo() + { + Console.WriteLine(); + } - void Foo() - { - Console.WriteLine(); - } + public int I() => 5; + } - public int I() => 5; -} -"; - var codeAfterMove = @"// Banner Text -using System; + """; + var codeAfterMove = """ + // Banner Text + using System; -partial class Class1 -// Leading comment -{ + partial class Class1 + // Leading comment + { - void Foo() - { - Console.WriteLine(); - } + void Foo() + { + Console.WriteLine(); + } - public int I() => 5; -} -"; + public int I() => 5; + } + + """; var expectedDocumentName = "Class2.cs"; - var destinationDocumentText = @"// Banner Text -partial class Class1 -{ - class Class2 - { - } -} -"; + var destinationDocumentText = """ + // Banner Text + partial class Class1 + { + class Class2 + { + } + } + + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1269,47 +1453,53 @@ await TestMoveTypeToNewFileAsync( public async Task TestLeadingCommentInContainer2() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; -class Class1 -{ // Leading comment - class [||]Class2 - { - } + class Class1 + { // Leading comment + class [||]Class2 + { + } - void Foo() - { - Console.WriteLine(); - } + void Foo() + { + Console.WriteLine(); + } - public int I() => 5; -} -"; - var codeAfterMove = @"// Banner Text -using System; + public int I() => 5; + } -partial class Class1 -{ // Leading comment + """; + var codeAfterMove = """ + // Banner Text + using System; - void Foo() - { - Console.WriteLine(); - } + partial class Class1 + { // Leading comment - public int I() => 5; -} -"; + void Foo() + { + Console.WriteLine(); + } + + public int I() => 5; + } + + """; var expectedDocumentName = "Class2.cs"; - var destinationDocumentText = @"// Banner Text -partial class Class1 -{ - class Class2 - { - } -} -"; + var destinationDocumentText = """ + // Banner Text + partial class Class1 + { + class Class2 + { + } + } + + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1319,49 +1509,55 @@ await TestMoveTypeToNewFileAsync( public async Task TestTrailingCommentInContainer() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; -class Class1 -{ - class [||]Class2 - { - } + class Class1 + { + class [||]Class2 + { + } - void Foo() - { - Console.WriteLine(); - } + void Foo() + { + Console.WriteLine(); + } - public int I() => 5; - // End of class document -} -"; - var codeAfterMove = @"// Banner Text -using System; + public int I() => 5; + // End of class document + } -partial class Class1 -{ + """; + var codeAfterMove = """ + // Banner Text + using System; - void Foo() - { - Console.WriteLine(); - } + partial class Class1 + { - public int I() => 5; - // End of class document -} -"; + void Foo() + { + Console.WriteLine(); + } + + public int I() => 5; + // End of class document + } + + """; var expectedDocumentName = "Class2.cs"; - var destinationDocumentText = @"// Banner Text -partial class Class1 -{ - class Class2 - { - } -} -"; + var destinationDocumentText = """ + // Banner Text + partial class Class1 + { + class Class2 + { + } + } + + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1371,46 +1567,52 @@ await TestMoveTypeToNewFileAsync( public async Task TestTrailingCommentInContainer2() { var code = -@"// Banner Text -using System; + """ + // Banner Text + using System; -class Class1 -{ - class [||]Class2 - { - } + class Class1 + { + class [||]Class2 + { + } - void Foo() - { - Console.WriteLine(); - } + void Foo() + { + Console.WriteLine(); + } - public int I() => 5; -} // End of class document -"; - var codeAfterMove = @"// Banner Text -using System; + public int I() => 5; + } // End of class document -partial class Class1 -{ + """; + var codeAfterMove = """ + // Banner Text + using System; - void Foo() - { - Console.WriteLine(); - } + partial class Class1 + { + + void Foo() + { + Console.WriteLine(); + } + + public int I() => 5; + } // End of class document - public int I() => 5; -} // End of class document -"; + """; var expectedDocumentName = "Class2.cs"; - var destinationDocumentText = @"// Banner Text -partial class Class1 -{ - class Class2 - { - } -}"; + var destinationDocumentText = """ + // Banner Text + partial class Class1 + { + class Class2 + { + } + } + """; await TestMoveTypeToNewFileAsync( code, codeAfterMove, expectedDocumentName, destinationDocumentText); @@ -1420,18 +1622,22 @@ await TestMoveTypeToNewFileAsync( public async Task MoveRecordToNewFilePreserveUsings() { var code = -@"using System; + """ + using System; -[||]record CacheContext(String Message); + [||]record CacheContext(String Message); -class Program { }"; + class Program { } + """; var codeAfterMove = @"class Program { }"; var expectedDocumentName = "CacheContext.cs"; - var destinationDocumentText = @"using System; + var destinationDocumentText = """ + using System; + + record CacheContext(String Message); -record CacheContext(String Message); -"; + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1440,18 +1646,22 @@ record CacheContext(String Message); public async Task MoveClassToNewFilePreserveUsings_PrimaryConstructor() { var code = -@"using System; + """ + using System; -[||]class CacheContext(String Message); + [||]class CacheContext(String Message); -class Program { }"; + class Program { } + """; var codeAfterMove = @"class Program { }"; var expectedDocumentName = "CacheContext.cs"; - var destinationDocumentText = @"using System; + var destinationDocumentText = """ + using System; -class CacheContext(String Message); -"; + class CacheContext(String Message); + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1460,18 +1670,22 @@ class CacheContext(String Message); public async Task MoveStructToNewFilePreserveUsings_PrimaryConstructor() { var code = -@"using System; + """ + using System; -[||]struct CacheContext(String Message); + [||]struct CacheContext(String Message); -class Program { }"; + class Program { } + """; var codeAfterMove = @"class Program { }"; var expectedDocumentName = "CacheContext.cs"; - var destinationDocumentText = @"using System; + var destinationDocumentText = """ + using System; + + struct CacheContext(String Message); -struct CacheContext(String Message); -"; + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1480,18 +1694,24 @@ struct CacheContext(String Message); public async Task MoveInterfaceToNewFilePreserveUsings_PrimaryConstructor() { var code = -@"using System; + """ + using System; -[||]interface CacheContext(String Message); + [||]interface CacheContext(String Message); -class Program { }"; - var codeAfterMove = @"using System; + class Program { } + """; + var codeAfterMove = """ + using System; -class Program { }"; + class Program { } + """; var expectedDocumentName = "CacheContext.cs"; - var destinationDocumentText = @"interface CacheContext(String Message); -"; + var destinationDocumentText = """ + interface CacheContext(String Message); + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1499,31 +1719,37 @@ class Program { }"; [WpfFact] public async Task MoveClassInTopLevelStatements() { - var code = @" -using ConsoleApp1; -using System; + var code = """ -var c = new C(); -Console.WriteLine(c.Hello); + using ConsoleApp1; + using System; -class [||]C -{ - public string Hello => ""Hello""; -}"; + var c = new C(); + Console.WriteLine(c.Hello); + + class [||]C + { + public string Hello => "Hello"; + } + """; + + var codeAfterMove = """ - var codeAfterMove = @" -using ConsoleApp1; -using System; + using ConsoleApp1; + using System; -var c = new C(); -Console.WriteLine(c.Hello); -"; + var c = new C(); + Console.WriteLine(c.Hello); + + """; var expectedDocumentName = "C.cs"; - var destinationDocumentText = @"class C -{ - public string Hello => ""Hello""; -}"; + var destinationDocumentText = """ + class C + { + public string Hello => "Hello"; + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1531,13 +1757,15 @@ class [||]C [WpfFact] public async Task MissingInTopLevelStatementsOnly() { - var code = @" -using ConsoleApp1; -using System; + var code = """ -var c = new object(); -[||]Console.WriteLine(c.ToString()); -"; + using ConsoleApp1; + using System; + + var c = new object(); + [||]Console.WriteLine(c.ToString()); + + """; await TestMissingAsync(code); } @@ -1545,39 +1773,45 @@ public async Task MissingInTopLevelStatementsOnly() [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/55544")] public async Task MoveInNamespace_WithAttributes1() { - var code = @" -using Sytem.Reflection; + var code = """ -[assembly: AssemblyCompany("")] -namespace N -{ - class A - { - } + using Sytem.Reflection; - class [||]B - { - } -}"; + [assembly: AssemblyCompany(")] + namespace N + { + class A + { + } + + class [||]B + { + } + } + """; - var codeAfterMove = @" -using Sytem.Reflection; + var codeAfterMove = """ -[assembly: AssemblyCompany("")] -namespace N -{ - class A - { - } -}"; + using Sytem.Reflection; + + [assembly: AssemblyCompany(")] + namespace N + { + class A + { + } + } + """; var expectedDocumentName = "B.cs"; - var destinationDocumentText = @"namespace N -{ - class B - { - } -}"; + var destinationDocumentText = """ + namespace N + { + class B + { + } + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1585,41 +1819,47 @@ class B [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/55544")] public async Task MoveInNamespace_WithAttributes2() { - var code = @" -using Sytem.Reflection; + var code = """ -[assembly: AssemblyCompany("")] -namespace N -{ - class A - { - } + using Sytem.Reflection; - [Test] - class [||]B - { - } -}"; + [assembly: AssemblyCompany(")] + namespace N + { + class A + { + } + + [Test] + class [||]B + { + } + } + """; - var codeAfterMove = @" -using Sytem.Reflection; + var codeAfterMove = """ -[assembly: AssemblyCompany("")] -namespace N -{ - class A - { - } -}"; + using Sytem.Reflection; + + [assembly: AssemblyCompany(")] + namespace N + { + class A + { + } + } + """; var expectedDocumentName = "B.cs"; - var destinationDocumentText = @"namespace N -{ - [Test] - class B - { - } -}"; + var destinationDocumentText = """ + namespace N + { + [Test] + class B + { + } + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1627,36 +1867,42 @@ class B [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/55544")] public async Task MoveInNamespace_WithAttributes3() { - var code = @" -namespace N -{ - class A - { - } + var code = """ - [Test] - class [||]B - { - } -}"; + namespace N + { + class A + { + } - var codeAfterMove = @" -namespace N -{ - class A - { - } -}"; + [Test] + class [||]B + { + } + } + """; + + var codeAfterMove = """ + + namespace N + { + class A + { + } + } + """; var expectedDocumentName = "B.cs"; - var destinationDocumentText = @" -namespace N -{ - [Test] - class B - { - } -}"; + var destinationDocumentText = """ + + namespace N + { + [Test] + class B + { + } + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1664,27 +1910,33 @@ class B [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/55544")] public async Task MoveTopLevel_WithAttributes1() { - var code = @" -[Test] -class [||]A -{ -} + var code = """ -class B -{ -}"; + [Test] + class [||]A + { + } - var codeAfterMove = @" -class B -{ -}"; + class B + { + } + """; + + var codeAfterMove = """ + + class B + { + } + """; var expectedDocumentName = "A.cs"; - var destinationDocumentText = @"[Test] -class A -{ -} -"; + var destinationDocumentText = """ + [Test] + class A + { + } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1692,29 +1944,35 @@ class A [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/55544")] public async Task MoveTopLevel_WithAttributes2() { - var code = @" -[Test] -class [||]A -{ -} + var code = """ -[Test] -class B -{ -}"; + [Test] + class [||]A + { + } - var codeAfterMove = @" -[Test] -class B -{ -}"; + [Test] + class B + { + } + """; + + var codeAfterMove = """ + + [Test] + class B + { + } + """; var expectedDocumentName = "A.cs"; - var destinationDocumentText = @"[Test] -class A -{ -} -"; + var destinationDocumentText = """ + [Test] + class A + { + } + + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } @@ -1727,25 +1985,31 @@ class A [InlineData("record")] public async Task MoveNestedTypeFromInterface(string memberType) { - var code = $@" -interface I -{{ - {memberType} [||]Member - {{ - }} -}}"; - var codeAfterMove = @" -partial interface I -{ -}"; + var code = $$""" + + interface I + { + {{memberType}} [||]Member + { + } + } + """; + var codeAfterMove = """ + + partial interface I + { + } + """; var expectedDocumentName = "Member.cs"; - var destinationDocumentText = $@" -partial interface I -{{ - {memberType} Member - {{ - }} -}}"; + var destinationDocumentText = $$""" + + partial interface I + { + {{memberType}} Member + { + } + } + """; await TestMoveTypeToNewFileAsync(code, codeAfterMove, expectedDocumentName, destinationDocumentText); } } diff --git a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs b/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs index 51b73da12df25..d74102910a0dd 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Threading.Tasks; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -23,19 +21,20 @@ public async Task ChangeNamespace_InvalidFolderName1() // No change namespace action because the folder name is not valid identifier var (folder, filePath) = CreateDocumentFilePath(["3B", "C"], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} -}} - - -"; + $$""" + + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + } + + + + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal: null); } @@ -48,19 +47,20 @@ public async Task ChangeNamespace_InvalidFolderName2() // No change namespace action because the folder name is not valid identifier var (folder, filePath) = CreateDocumentFilePath(["B.3C", "D"], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} -}} - - -"; + $$""" + + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + } + + + + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal: null); } @@ -72,26 +72,29 @@ public async Task ChangeNamespace_SingleDocumentNoReference() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} -}} - -"; + $$""" + + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - class Class1 - { - } -}"; + """ + namespace A.B.C + { + class Class1 + { + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -103,26 +106,26 @@ public async Task ChangeNamespace_SingleDocumentNoReference_FileScopedNamespace( var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace}; - -class Class1 -{{ -}} - - -"; + $$""" + + + namespace [||]{{declaredNamespace}}; + + class Class1 + { + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C; + """ + namespace A.B.C; -class Class1 -{ -} -"; + class Class1 + { + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -134,46 +137,49 @@ public async Task ChangeNamespace_SingleDocumentLocalReference() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - delegate void D1; - - interface Class1 - {{ - void M1(); - }} - - class Class2 : {declaredNamespace}.Class1 - {{ - {declaredNamespace}.D1 d; - - void {declaredNamespace}.Class1.M1(){{}} - }} -}} - -"; + $$""" + + + + namespace [||]{{declaredNamespace}} + { + delegate void D1; + + interface Class1 + { + void M1(); + } + + class Class2 : {{declaredNamespace}}.Class1 + { + {{declaredNamespace}}.D1 d; + + void {{declaredNamespace}}.Class1.M1(){} + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - delegate void D1; - - interface Class1 - { - void M1(); - } - - class Class2 : Class1 - { - D1 d; - - void Class1.M1() { } - } -}"; + """ + namespace A.B.C + { + delegate void D1; + + interface Class1 + { + void M1(); + } + + class Class2 : Class1 + { + D1 d; + + void Class1.M1() { } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -186,71 +192,73 @@ public async Task ChangeNamespace_WithCrefReference() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - /// <summary> - /// See <see cref=""Class1""/> - /// See <see cref=""{declaredNamespace}.Class1""/> - /// See <see cref=""global::{declaredNamespace}.Class1""/> - /// See <see cref=""global::{declaredNamespace}.Class1.M1""/> - /// </summary> - public class Class1 - {{ - public void M1() {{ }} - }} -}} - -namespace Foo -{{ - using {declaredNamespace}; - - /// <summary> - /// See <see cref=""Class1""/> - /// See <see cref=""{declaredNamespace}.Class1""/> - /// See <see cref=""global::{declaredNamespace}.Class1""/> - /// See <see cref=""global::{declaredNamespace}.Class1.M1""/> - /// </summary> - class RefClass - {{ - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + /// <summary> + /// See <see cref="Class1"/> + /// See <see cref="{{declaredNamespace}}.Class1"/> + /// See <see cref="global::{{declaredNamespace}}.Class1"/> + /// See <see cref="global::{{declaredNamespace}}.Class1.M1"/> + /// </summary> + public class Class1 + { + public void M1() { } + } + } + namespace Foo + { + using {{declaredNamespace}}; + + /// <summary> + /// See <see cref="Class1"/> + /// See <see cref="{{declaredNamespace}}.Class1"/> + /// See <see cref="global::{{declaredNamespace}}.Class1"/> + /// See <see cref="global::{{declaredNamespace}}.Class1.M1"/> + /// </summary> + class RefClass + { + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - /// - /// See - /// See - /// See - /// See - /// - public class Class1 - { - public void M1() { } - } -}"; + """ + namespace A.B.C + { + /// + /// See + /// See + /// See + /// See + /// + public class Class1 + { + public void M1() { } + } + } + """; var expectedSourceReference = -@" -namespace Foo -{ - using A.B.C; - - /// - /// See - /// See - /// See - /// See - /// - class RefClass - { - } -}"; + """ + namespace Foo + { + using A.B.C; + + /// + /// See + /// See + /// See + /// See + /// + class RefClass + { + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -262,57 +270,59 @@ public async Task ChangeNamespace_WithCrefReferencesInVB() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - /// <summary> - /// See <see cref=""Class1""/> - /// See <see cref=""{declaredNamespace}.Class1""/> - /// </summary> - public class Class1 - {{ - }} -}} - - - -Imports {declaredNamespace} - -''' <summary> -''' See <see cref=""Class1""/> -''' See <see cref=""{declaredNamespace}.Class1""/> -''' </summary> -Public Class VBClass - Public ReadOnly Property C1 As Class1 -End Class - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + /// <summary> + /// See <see cref="Class1"/> + /// See <see cref="{{declaredNamespace}}.Class1"/> + /// </summary> + public class Class1 + { + } + } + + + Imports {{declaredNamespace}} + + ''' <summary> + ''' See <see cref="Class1"/> + ''' See <see cref="{{declaredNamespace}}.Class1"/> + ''' </summary> + Public Class VBClass + Public ReadOnly Property C1 As Class1 + End Class + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - /// - /// See - /// See - /// - public class Class1 - { - } -}"; + """ + namespace A.B.C + { + /// + /// See + /// See + /// + public class Class1 + { + } + } + """; var expectedSourceReference = -@" -Imports A.B.C - -''' -''' See -''' See -''' -Public Class VBClass - Public ReadOnly Property C1 As Class1 -End Class"; + """ + Imports A.B.C + + ''' + ''' See + ''' See + ''' + Public Class VBClass + Public ReadOnly Property C1 As Class1 + End Class + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -325,51 +335,54 @@ public async Task ChangeNamespace_ReferencingTypesDeclaredInOtherDocument() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - private Class2 c2; - private Class3 c3; - private Class4 c4; - }} -}} - -namespace Foo -{{ - class Class2 {{}} - - namespace Bar - {{ - class Class3 {{}} - - namespace Baz - {{ - class Class4 {{}} - }} - }} -}} - -"; + $$""" + + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + private Class2 c2; + private Class3 c3; + private Class4 c4; + } + } + + namespace Foo + { + class Class2 {} + + namespace Bar + { + class Class3 {} + + namespace Baz + { + class Class4 {} + } + } + } + + + """; var expectedSourceOriginal = -@"using Foo; -using Foo.Bar; -using Foo.Bar.Baz; - -namespace A.B.C -{ - class Class1 - { - private Class2 c2; - private Class3 c3; - private Class4 c4; - } -}"; + """ + using Foo; + using Foo.Bar; + using Foo.Bar.Baz; + + namespace A.B.C + { + class Class1 + { + private Class2 c2; + private Class3 c3; + private Class4 c4; + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -382,51 +395,54 @@ public async Task ChangeNamespace_ReferencingQualifiedTypesDeclaredInOtherDocume var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - private Foo.Class2 c2; - private Foo.Bar.Class3 c3; - private Foo.Bar.Baz.Class4 c4; - }} -}} - -namespace Foo -{{ - class Class2 {{}} - - namespace Bar - {{ - class Class3 {{}} - - namespace Baz - {{ - class Class4 {{}} - }} - }} -}} - -"; + $$""" + + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + private Foo.Class2 c2; + private Foo.Bar.Class3 c3; + private Foo.Bar.Baz.Class4 c4; + } + } + + namespace Foo + { + class Class2 {} + + namespace Bar + { + class Class3 {} + + namespace Baz + { + class Class4 {} + } + } + } + + + """; var expectedSourceOriginal = -@"using Foo; -using Foo.Bar; -using Foo.Bar.Baz; - -namespace A.B.C -{ - class Class1 - { - private Class2 c2; - private Class3 c3; - private Class4 c4; - } -}"; + """ + using Foo; + using Foo.Bar; + using Foo.Bar.Baz; + + namespace A.B.C + { + class Class1 + { + private Class2 c2; + private Class3 c3; + private Class4 c4; + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -439,65 +455,67 @@ public async Task ChangeNamespace_WithReferencesInOtherDocument() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} - - class Class2 - {{ - }} -}} - -using Foo.Bar.Baz; - -namespace Foo -{{ - class RefClass - {{ - private Class1 c1; - - void M1() - {{ - Bar.Baz.Class2 c2 = null; - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + + class Class2 + { + } + } + using Foo.Bar.Baz; + + namespace Foo + { + class RefClass + { + private Class1 c1; + + void M1() + { + Bar.Baz.Class2 c2 = null; + } + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - class Class1 - { - } - - class Class2 - { - } -}"; + """ + namespace A.B.C + { + class Class1 + { + } + + class Class2 + { + } + } + """; var expectedSourceReference = -@" -using A.B.C; - -namespace Foo -{ - class RefClass - { - private Class1 c1; - - void M1() - { - Class2 c2 = null; - } - } -}"; + """ + using A.B.C; + + namespace Foo + { + class RefClass + { + private Class1 c1; + + void M1() + { + Class2 c2 = null; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -510,49 +528,51 @@ public async Task ChangeNamespace_WithQualifiedReferencesInOtherDocument() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - interface Interface1 - {{ - void M1(Interface1 c1); - }} -}} - -namespace Foo -{{ - using {declaredNamespace}; - - class RefClass : Interface1 - {{ - void {declaredNamespace}.Interface1.M1(Interface1 c1){{}} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + interface Interface1 + { + void M1(Interface1 c1); + } + } + namespace Foo + { + using {{declaredNamespace}}; + + class RefClass : Interface1 + { + void {{declaredNamespace}}.Interface1.M1(Interface1 c1){} + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - interface Interface1 - { - void M1(Interface1 c1); - } -}"; + """ + namespace A.B.C + { + interface Interface1 + { + void M1(Interface1 c1); + } + } + """; var expectedSourceReference = -@" -namespace Foo -{ - using A.B.C; - - class RefClass : Interface1 - { - void Interface1.M1(Interface1 c1){} - } -}"; + """ + namespace Foo + { + using A.B.C; + + class RefClass : Interface1 + { + void Interface1.M1(Interface1 c1){} + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -565,65 +585,67 @@ public async Task ChangeNamespace_ChangeUsingsInMultipleContainers() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} -}} - -namespace NS1 -{{ - using Foo.Bar.Baz; - - class Class2 - {{ - Class1 c2; - }} - - namespace NS2 - {{ - using Foo.Bar.Baz; - - class Class2 - {{ - Class1 c1; - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + } + namespace NS1 + { + using Foo.Bar.Baz; + + class Class2 + { + Class1 c2; + } + + namespace NS2 + { + using Foo.Bar.Baz; + + class Class2 + { + Class1 c1; + } + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - class Class1 - { - } -}"; + """ + namespace A.B.C + { + class Class1 + { + } + } + """; var expectedSourceReference = -@" -namespace NS1 -{ - using A.B.C; - - class Class2 - { - Class1 c2; - } - - namespace NS2 - { - class Class2 - { - Class1 c1; - } - } -}"; + """ + namespace NS1 + { + using A.B.C; + + class Class2 + { + Class1 c2; + } + + namespace NS2 + { + class Class2 + { + Class1 c1; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -636,68 +658,70 @@ public async Task ChangeNamespace_WithAliasReferencesInOtherDocument() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} - - class Class2 - {{ - }} -}} - -using System; -using Class1Alias = Foo.Bar.Baz.Class1; - -namespace Foo -{{ - class RefClass - {{ - private Class1Alias c1; - - void M1() - {{ - Bar.Baz.Class2 c2 = null; - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + + class Class2 + { + } + } + using System; + using Class1Alias = Foo.Bar.Baz.Class1; + + namespace Foo + { + class RefClass + { + private Class1Alias c1; + + void M1() + { + Bar.Baz.Class2 c2 = null; + } + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - class Class1 - { - } - - class Class2 - { - } -}"; + """ + namespace A.B.C + { + class Class1 + { + } + + class Class2 + { + } + } + """; var expectedSourceReference = -@" -using System; -using A.B.C; -using Class1Alias = A.B.C.Class1; - -namespace Foo -{ - class RefClass - { - private Class1Alias c1; - - void M1() - { - Class2 c2 = null; - } - } -}"; + """ + using System; + using A.B.C; + using Class1Alias = A.B.C.Class1; + + namespace Foo + { + class RefClass + { + private Class1Alias c1; + + void M1() + { + Class2 c2 = null; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -709,36 +733,37 @@ public async Task ChangeToGlobalNamespace_SingleDocumentNoRef() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -using System; - -// Comments before declaration. -namespace [||]{declaredNamespace} -{{ // Comments after opening brace - class Class1 - {{ - }} - // Comments before closing brace -}} // Comments after declaration. - - -"; + $$""" + + + using System; + + // Comments before declaration. + namespace [||]{{declaredNamespace}} + { // Comments after opening brace + class Class1 + { + } + // Comments before closing brace + } // Comments after declaration. + + + + """; var expectedSourceOriginal = -@" -using System; - -// Comments before declaration. -// Comments after opening brace -class Class1 -{ -} -// Comments before closing brace -// Comments after declaration. -"; + """ + using System; + + // Comments before declaration. + // Comments after opening brace + class Class1 + { + } + // Comments before closing brace + // Comments after declaration. + + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -750,44 +775,46 @@ public async Task ChangeToGlobalNamespace_SingleDocumentLocalRef() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - delegate void D1; - - interface Class1 - {{ - void M1(); - }} - - class Class2 : {declaredNamespace}.Class1 - {{ - global::{declaredNamespace}.D1 d; - - void {declaredNamespace}.Class1.M1() {{ }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + delegate void D1; + + interface Class1 + { + void M1(); + } + + class Class2 : {{declaredNamespace}}.Class1 + { + global::{{declaredNamespace}}.D1 d; + + void {{declaredNamespace}}.Class1.M1() { } + } + } + + + """; var expectedSourceOriginal = -@"delegate void D1; + """ + delegate void D1; -interface Class1 -{ - void M1(); -} + interface Class1 + { + void M1(); + } -class Class2 : Class1 -{ - global::D1 d; + class Class2 : Class1 + { + global::D1 d; - void Class1.M1() { } -} -"; + void Class1.M1() { } + } + + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -800,60 +827,63 @@ public async Task ChangeToGlobalNamespace_WithReferencesInOtherDocument() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} - - class Class2 - {{ - }} -}} - -using Foo.Bar.Baz; - -namespace Foo -{{ - class RefClass - {{ - private Class1 c1; - - void M1() - {{ - Bar.Baz.Class2 c2 = null; - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + + class Class2 + { + } + } + using Foo.Bar.Baz; + + namespace Foo + { + class RefClass + { + private Class1 c1; + + void M1() + { + Bar.Baz.Class2 c2 = null; + } + } + } + + + """; var expectedSourceOriginal = -@"class Class1 -{ -} + """ + class Class1 + { + } -class Class2 -{ -} -"; - var expectedSourceReference = -@"namespace Foo -{ - class RefClass - { - private Class1 c1; + class Class2 + { + } - void M1() - { - Class2 c2 = null; - } - } -}"; + """; + var expectedSourceReference = + """ + namespace Foo + { + class RefClass + { + private Class1 c1; + + void M1() + { + Class2 c2 = null; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -866,45 +896,47 @@ public async Task ChangeToGlobalNamespace_WithQualifiedReferencesInOtherDocument var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - interface Interface1 - {{ - void M1(Interface1 c1); - }} -}} - -namespace Foo -{{ - using {declaredNamespace}; - - class RefClass : Interface1 - {{ - void {declaredNamespace}.Interface1.M1(Interface1 c1){{}} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + interface Interface1 + { + void M1(Interface1 c1); + } + } + namespace Foo + { + using {{declaredNamespace}}; + + class RefClass : Interface1 + { + void {{declaredNamespace}}.Interface1.M1(Interface1 c1){} + } + } + + + """; var expectedSourceOriginal = -@"interface Interface1 -{ - void M1(Interface1 c1); -} -"; + """ + interface Interface1 + { + void M1(Interface1 c1); + } + + """; var expectedSourceReference = -@" -namespace Foo -{ - class RefClass : Interface1 - { - void Interface1.M1(Interface1 c1){} - } -}"; + """ + namespace Foo + { + class RefClass : Interface1 + { + void Interface1.M1(Interface1 c1){} + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -917,51 +949,53 @@ public async Task ChangeToGlobalNamespace_WithReferenceAndConflictDeclarationInO var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class MyClass - {{ - }} -}} - -namespace Foo -{{ - using {declaredNamespace}; - - class RefClass - {{ - Foo.Bar.Baz.MyClass c; - }} - - class MyClass - {{ - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + class MyClass + { + } + } + namespace Foo + { + using {{declaredNamespace}}; + + class RefClass + { + Foo.Bar.Baz.MyClass c; + } + + class MyClass + { + } + } + + + """; var expectedSourceOriginal = -@"class MyClass -{ -} -"; - var expectedSourceReference = -@" -namespace Foo -{ - class RefClass - { - global::MyClass c; - } + """ + class MyClass + { + } - class MyClass - { - } -}"; + """; + var expectedSourceReference = + """ + namespace Foo + { + class RefClass + { + global::MyClass c; + } + + class MyClass + { + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -974,49 +1008,50 @@ public async Task ChangeToGlobalNamespace_ReferencingTypesDeclaredInOtherDocumen var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - private Class2 c2; - private Class3 c3; - private Class4 c4; - }} -}} - -namespace Foo -{{ - class Class2 {{}} - - namespace Bar - {{ - class Class3 {{}} - - namespace Baz - {{ - class Class4 {{}} - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + private Class2 c2; + private Class3 c3; + private Class4 c4; + } + } + namespace Foo + { + class Class2 {} + + namespace Bar + { + class Class3 {} + + namespace Baz + { + class Class4 {} + } + } + } + + + """; var expectedSourceOriginal = -@"using Foo; -using Foo.Bar; -using Foo.Bar.Baz; - -class Class1 -{ - private Class2 c2; - private Class3 c3; - private Class4 c4; -} -"; + """ + using Foo; + using Foo.Bar; + using Foo.Bar.Baz; + + class Class1 + { + private Class2 c2; + private Class3 c3; + private Class4 c4; + } + + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -1029,61 +1064,63 @@ public async Task ChangeToGlobalNamespace_ChangeUsingsInMultipleContainers() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} -}} - -namespace NS1 -{{ - using Foo.Bar.Baz; - - class Class2 - {{ - Class1 c2; - }} - - namespace NS2 - {{ - using Foo.Bar.Baz; - - class Class2 - {{ - Class1 c1; - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + } + namespace NS1 + { + using Foo.Bar.Baz; + + class Class2 + { + Class1 c2; + } + + namespace NS2 + { + using Foo.Bar.Baz; + + class Class2 + { + Class1 c1; + } + } + } + + + """; var expectedSourceOriginal = -@"class Class1 -{ -} -"; - var expectedSourceReference = -@" -namespace NS1 -{ - class Class2 - { - Class1 c2; - } + """ + class Class1 + { + } - namespace NS2 - { - class Class2 - { - Class1 c1; - } - } -}"; + """; + var expectedSourceReference = + """ + namespace NS1 + { + class Class2 + { + Class1 c2; + } + + namespace NS2 + { + class Class2 + { + Class1 c1; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1096,65 +1133,67 @@ public async Task ChangeToGlobalNamespace_WithAliasReferencesInOtherDocument() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - class Class1 - {{ - }} - - class Class2 - {{ - }} -}} - -using System; -using Class1Alias = Foo.Bar.Baz.Class1; - -namespace Foo -{{ - class RefClass - {{ - private Class1Alias c1; - - void M1() - {{ - Bar.Baz.Class2 c2 = null; - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + class Class1 + { + } + + class Class2 + { + } + } + using System; + using Class1Alias = Foo.Bar.Baz.Class1; + + namespace Foo + { + class RefClass + { + private Class1Alias c1; + + void M1() + { + Bar.Baz.Class2 c2 = null; + } + } + } + + + """; var expectedSourceOriginal = -@"class Class1 -{ -} - -class Class2 -{ -} -"; - var expectedSourceReference = -@" -using System; -using Class1Alias = Class1; + """ + class Class1 + { + } -namespace Foo -{ - class RefClass - { - private Class1Alias c1; + class Class2 + { + } - void M1() - { - Class2 c2 = null; - } - } -}"; + """; + var expectedSourceReference = + """ + using System; + using Class1Alias = Class1; + + namespace Foo + { + class RefClass + { + private Class1Alias c1; + + void M1() + { + Class2 c2 = null; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1164,29 +1203,30 @@ public async Task ChangeFromGlobalNamespace_SingleDocumentNoRef() var defaultNamespace = "A"; var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var code = -$@" - - - -using System; - -class [||]Class1 -{{ -}} - - -"; + $$""" + + + using System; + + class [||]Class1 + { + } + + + + """; var expectedSourceOriginal = -@" -using System; - -namespace A.B.C -{ - class Class1 - { - } -}"; + """ + using System; + + namespace A.B.C + { + class Class1 + { + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -1196,43 +1236,46 @@ public async Task ChangeFromGlobalNamespace_SingleDocumentLocalRef() var defaultNamespace = "A"; var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var code = -$@" - - - -delegate void [||]D1; - -interface Class1 -{{ - void M1(); -}} - -class Class2 : Class1 -{{ - D1 d; - - void Class1.M1() {{ }} -}} - -"; + $$""" + + + + delegate void [||]D1; + + interface Class1 + { + void M1(); + } + + class Class2 : Class1 + { + D1 d; + + void Class1.M1() { } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - delegate void D1; - - interface Class1 - { - void M1(); - } - - class Class2 : Class1 - { - D1 d; - - void Class1.M1() { } - } -}"; + """ + namespace A.B.C + { + delegate void D1; + + interface Class1 + { + void M1(); + } + + class Class2 : Class1 + { + D1 d; + + void Class1.M1() { } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -1244,60 +1287,62 @@ public async Task ChangeFromGlobalNamespace_WithReferencesInOtherDocument() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -class [||]Class1 -{{ -}} - -class Class2 -{{ -}} - -namespace Foo -{{ - class RefClass - {{ - private Class1 c1; - - void M1() - {{ - Class2 c2 = null; - }} - }} -}} - -"; + $$""" + + + class [||]Class1 + { + } + + class Class2 + { + } + namespace Foo + { + class RefClass + { + private Class1 c1; + + void M1() + { + Class2 c2 = null; + } + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - class Class1 - { - } - - class Class2 - { - } -}"; + """ + namespace A.B.C + { + class Class1 + { + } + + class Class2 + { + } + } + """; var expectedSourceReference = -@" -using A.B.C; - -namespace Foo -{ - class RefClass - { - private Class1 c1; - - void M1() - { - Class2 c2 = null; - } - } -}"; + """ + using A.B.C; + + namespace Foo + { + class RefClass + { + private Class1 c1; + + void M1() + { + Class2 c2 = null; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1308,44 +1353,46 @@ public async Task ChangeFromGlobalNamespace_WithQualifiedReferencesInOtherDocume var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -interface [||]Interface1 -{{ - void M1(Interface1 c1); -}} - -namespace Foo -{{ - class RefClass : Interface1 - {{ - void Interface1.M1(Interface1 c1){{}} - }} -}} - -"; + $$""" + + + interface [||]Interface1 + { + void M1(Interface1 c1); + } + namespace Foo + { + class RefClass : Interface1 + { + void Interface1.M1(Interface1 c1){} + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - interface Interface1 - { - void M1(Interface1 c1); - } -}"; + """ + namespace A.B.C + { + interface Interface1 + { + void M1(Interface1 c1); + } + } + """; var expectedSourceReference = -@" -using A.B.C; - -namespace Foo -{ - class RefClass : Interface1 - { - void Interface1.M1(Interface1 c1){} - } -}"; + """ + using A.B.C; + + namespace Foo + { + class RefClass : Interface1 + { + void Interface1.M1(Interface1 c1){} + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1356,45 +1403,48 @@ public async Task ChangeFromGlobalNamespace_ReferencingQualifiedTypesDeclaredInO var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -class [||]Class1 -{{ - private A.Class2 c2; - private A.B.Class3 c3; - private A.B.C.Class4 c4; -}} - - -namespace A -{{ - class Class2 {{}} - - namespace B - {{ - class Class3 {{}} - - namespace C - {{ - class Class4 {{}} - }} - }} -}} - -"; + $$""" + + + + class [||]Class1 + { + private A.Class2 c2; + private A.B.Class3 c3; + private A.B.C.Class4 c4; + } + + + namespace A + { + class Class2 {} + + namespace B + { + class Class3 {} + + namespace C + { + class Class4 {} + } + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - class Class1 - { - private Class2 c2; - private Class3 c3; - private Class4 c4; - } -}"; + """ + namespace A.B.C + { + class Class1 + { + private Class2 c2; + private Class3 c3; + private Class4 c4; + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal); } @@ -1406,65 +1456,67 @@ public async Task ChangeFromGlobalNamespace_ChangeUsingsInMultipleContainers() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -class [||]Class1 -{{ -}} - -namespace NS1 -{{ - using System; - - class Class2 - {{ - Class1 c2; - }} - - namespace NS2 - {{ - using System; - - class Class2 - {{ - Class1 c1; - }} - }} -}} - -"; + $$""" + + + class [||]Class1 + { + } + namespace NS1 + { + using System; + + class Class2 + { + Class1 c2; + } + + namespace NS2 + { + using System; + + class Class2 + { + Class1 c1; + } + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - class Class1 - { - } -}"; + """ + namespace A.B.C + { + class Class1 + { + } + } + """; var expectedSourceReference = -@" -namespace NS1 -{ - using System; - using A.B.C; - - class Class2 - { - Class1 c2; - } - - namespace NS2 - { - using System; - - class Class2 - { - Class1 c1; - } - } -}"; + """ + namespace NS1 + { + using System; + using A.B.C; + + class Class2 + { + Class1 c2; + } + + namespace NS2 + { + using System; + + class Class2 + { + Class1 c1; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1476,67 +1528,69 @@ public async Task ChangeFromGlobalNamespace_WithAliasReferencesInOtherDocument() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -class [||]Class1 -{{ -}} - -class Class2 -{{ -}} - -using Class1Alias = Class1; - -namespace Foo -{{ - using System; - - class RefClass - {{ - private Class1Alias c1; - - void M1() - {{ - Class2 c2 = null; - }} - }} -}} - -"; + $$""" + + + class [||]Class1 + { + } + + class Class2 + { + } + using Class1Alias = Class1; + + namespace Foo + { + using System; + + class RefClass + { + private Class1Alias c1; + + void M1() + { + Class2 c2 = null; + } + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - class Class1 - { - } - - class Class2 - { - } -}"; + """ + namespace A.B.C + { + class Class1 + { + } + + class Class2 + { + } + } + """; var expectedSourceReference = -@" -using A.B.C; -using Class1Alias = Class1; - -namespace Foo -{ - using System; - - class RefClass - { - private Class1Alias c1; - - void M1() - { - Class2 c2 = null; - } - } -}"; + """ + using A.B.C; + using Class1Alias = Class1; + + namespace Foo + { + using System; + + class RefClass + { + private Class1Alias c1; + + void M1() + { + Class2 c2 = null; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1548,41 +1602,43 @@ public async Task ChangeNamespace_WithReferencesInVBDocument() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - public class Class1 - {{ - }} -}} - - - -Imports {declaredNamespace} - -Public Class VBClass - Public ReadOnly Property C1 As Class1 -End Class - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + public class Class1 + { + } + } + + + Imports {{declaredNamespace}} + + Public Class VBClass + Public ReadOnly Property C1 As Class1 + End Class + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - public class Class1 - { - } -}"; + """ + namespace A.B.C + { + public class Class1 + { + } + } + """; var expectedSourceReference = -@" -Imports A.B.C + """ + Imports A.B.C -Public Class VBClass - Public ReadOnly Property C1 As Class1 -End Class"; + Public Class VBClass + Public ReadOnly Property C1 As Class1 + End Class + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1594,36 +1650,41 @@ public async Task ChangeNamespace_WithQualifiedReferencesInVBDocument() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - public class Class1 - {{ - }} -}} - - - -Public Class VBClass - Public ReadOnly Property C1 As A.B.C.D.Class1 -End Class - -"; + $$""" + + + + namespace [||]{{declaredNamespace}} + { + public class Class1 + { + } + } + + + + Public Class VBClass + Public ReadOnly Property C1 As A.B.C.D.Class1 + End Class + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - public class Class1 - { - } -}"; + """ + namespace A.B.C + { + public class Class1 + { + } + } + """; var expectedSourceReference = -@"Public Class VBClass - Public ReadOnly Property C1 As A.B.C.Class1 -End Class"; + """ + Public Class VBClass + Public ReadOnly Property C1 As A.B.C.Class1 + End Class + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1634,37 +1695,39 @@ public async Task ChangeFromGlobalNamespace_WithReferencesInVBDocument() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var code = -$@" - - - -public class [||]Class1 -{{ -}} - - - - -Public Class VBClass - Public ReadOnly Property C1 As Class1 -End Class - -"; + $$""" + + + public class [||]Class1 + { + } + + + + Public Class VBClass + Public ReadOnly Property C1 As Class1 + End Class + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - public class Class1 - { - } -}"; + """ + namespace A.B.C + { + public class Class1 + { + } + } + """; var expectedSourceReference = -@" -Imports A.B.C + """ + Imports A.B.C -Public Class VBClass - Public ReadOnly Property C1 As Class1 -End Class"; + Public Class VBClass + Public ReadOnly Property C1 As Class1 + End Class + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1675,53 +1738,55 @@ public async Task ChangeFromGlobalNamespace_WithCredReferences() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -/// <summary> -/// See <see cref=""Class1""/> -/// </summary> -class [||]Class1 -{{ -}} - -namespace Foo -{{ - /// <summary> - /// See <see cref=""Class1""/> - /// </summary> - class Bar - {{ - }} -}} - -"; + $$""" + + + /// <summary> + /// See <see cref="Class1"/> + /// </summary> + class [||]Class1 + { + } + namespace Foo + { + /// <summary> + /// See <see cref="Class1"/> + /// </summary> + class Bar + { + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - - /// - /// See - /// - class Class1 - { - } -}"; + """ + namespace A.B.C + { + + /// + /// See + /// + class Class1 + { + } + } + """; var expectedSourceReference = -@" -using A.B.C; - -namespace Foo -{ - /// - /// See - /// - class Bar - { - } -}"; + """ + using A.B.C; + + namespace Foo + { + /// + /// See + /// + class Bar + { + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1733,36 +1798,39 @@ public async Task ChangeToGlobalNamespace_WithReferencesInVBDocument() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - public class Class1 - {{ - }} -}} - - - -Imports {declaredNamespace} - -Public Class VBClass - Public ReadOnly Property C1 As Class1 -End Class - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + public class Class1 + { + } + } + + + Imports {{declaredNamespace}} + + Public Class VBClass + Public ReadOnly Property C1 As Class1 + End Class + + + """; var expectedSourceOriginal = -@"public class Class1 -{ -} -"; + """ + public class Class1 + { + } + + """; var expectedSourceReference = -@"Public Class VBClass - Public ReadOnly Property C1 As Class1 -End Class"; + """ + Public Class VBClass + Public ReadOnly Property C1 As Class1 + End Class + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1774,44 +1842,47 @@ public async Task ChangeToGlobalNamespace_WithReferenceAndConflictDeclarationInV var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - public class MyClass - {{ - }} -}} - - - -Namespace Foo - Public Class VBClass - Public ReadOnly Property C1 As Foo.Bar.Baz.MyClass - End Class - - Public Class MyClass - End Class -End Namespace - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + public class MyClass + { + } + } + + + Namespace Foo + Public Class VBClass + Public ReadOnly Property C1 As Foo.Bar.Baz.MyClass + End Class + + Public Class MyClass + End Class + End Namespace + + + """; var expectedSourceOriginal = -@"public class MyClass -{ -} -"; + """ + public class MyClass + { + } + + """; var expectedSourceReference = -@"Namespace Foo - Public Class VBClass - Public ReadOnly Property C1 As Global.MyClass - End Class - - Public Class MyClass - End Class -End Namespace"; + """ + Namespace Foo + Public Class VBClass + Public ReadOnly Property C1 As Global.MyClass + End Class + + Public Class MyClass + End Class + End Namespace + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1824,57 +1895,59 @@ public async Task ChangeToGlobalNamespace_WithCredReferences() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - /// <summary> - /// See <see cref=""Class1""/> - /// See <see cref=""{declaredNamespace}.Class1""/> - /// </summary> - public class Class1 - {{ - }} -}} - -namespace Foo -{{ - using {declaredNamespace}; - - /// <summary> - /// See <see cref=""Class1""/> - /// See <see cref=""{declaredNamespace}.Class1""/> - /// </summary> - class RefClass - {{ - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + /// <summary> + /// See <see cref="Class1"/> + /// See <see cref="{{declaredNamespace}}.Class1"/> + /// </summary> + public class Class1 + { + } + } + namespace Foo + { + using {{declaredNamespace}}; + + /// <summary> + /// See <see cref="Class1"/> + /// See <see cref="{{declaredNamespace}}.Class1"/> + /// </summary> + class RefClass + { + } + } + + + """; var expectedSourceOriginal = -@"/// -/// See -/// See -/// -public class Class1 -{ -} -"; + """ + /// + /// See + /// See + /// + public class Class1 + { + } + + """; var expectedSourceReference = -@" -namespace Foo -{ - /// - /// See - /// See - /// - class RefClass - { - } -}"; + """ + namespace Foo + { + /// + /// See + /// See + /// + class RefClass + { + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1886,50 +1959,52 @@ public async Task ChangeNamespace_ExtensionMethodInReducedForm() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{defaultNamespace} -{{ - public static class Extensions - {{ - public static bool Foo(this Class1 c1) => true; - }} -}} - -namespace {defaultNamespace} -{{ - using System; - - public class Class1 - {{ - public bool Bar(Class1 c1) => c1.Foo(); - }} -}} - -"; + $$""" + + + namespace [||]{{defaultNamespace}} + { + public static class Extensions + { + public static bool Foo(this Class1 c1) => true; + } + } + namespace {{defaultNamespace}} + { + using System; + + public class Class1 + { + public bool Bar(Class1 c1) => c1.Foo(); + } + } + + + """; var expectedSourceOriginal = -$@"namespace A.B.C -{{ - public static class Extensions - {{ - public static bool Foo(this Class1 c1) => true; - }} -}}"; + $$""" + namespace A.B.C + { + public static class Extensions + { + public static bool Foo(this Class1 c1) => true; + } + } + """; var expectedSourceReference = -$@" -namespace {defaultNamespace} -{{ - using System; - using A.B.C; - - public class Class1 - {{ - public bool Bar(Class1 c1) => c1.Foo(); - }} -}}"; + $$""" + namespace {{defaultNamespace}} + { + using System; + using A.B.C; + + public class Class1 + { + public bool Bar(Class1 c1) => c1.Foo(); + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1941,50 +2016,52 @@ public async Task ChangeNamespace_ExternsionMethodInRegularForm() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]A -{{ - public static class Extensions - {{ - public static bool Foo(this Class1 c1) => true; - }} -}} - -using System; - -namespace A -{{ - public class Class1 - {{ - public bool Bar(Class1 c1) => Extensions.Foo(c1); - }} -}} - -"; + $$""" + + + namespace [||]A + { + public static class Extensions + { + public static bool Foo(this Class1 c1) => true; + } + } + using System; + + namespace A + { + public class Class1 + { + public bool Bar(Class1 c1) => Extensions.Foo(c1); + } + } + + + """; var expectedSourceOriginal = -$@"namespace A.B.C -{{ - public static class Extensions - {{ - public static bool Foo(this Class1 c1) => true; - }} -}}"; + $$""" + namespace A.B.C + { + public static class Extensions + { + public static bool Foo(this Class1 c1) => true; + } + } + """; var expectedSourceReference = -$@" -using System; -using A.B.C; - -namespace A -{{ - public class Class1 - {{ - public bool Bar(Class1 c1) => Extensions.Foo(c1); - }} -}}"; + $$""" + using System; + using A.B.C; + + namespace A + { + public class Class1 + { + public bool Bar(Class1 c1) => Extensions.Foo(c1); + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -1996,56 +2073,58 @@ public async Task ChangeNamespace_ContainsBothTypeAndExternsionMethod() var (folder, filePath) = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]A -{{ - public static class Extensions - {{ - public static bool Foo(this Class1 c1) => true; - }} - - public class Class2 - {{ }} -}} - -using System; - -namespace A -{{ - public class Class1 - {{ - public bool Bar(Class1 c1, Class2 c2) => c2 == null ? c1.Foo() : true; - }} -}} - -"; + $$""" + + + namespace [||]A + { + public static class Extensions + { + public static bool Foo(this Class1 c1) => true; + } + + public class Class2 + { } + } + using System; + + namespace A + { + public class Class1 + { + public bool Bar(Class1 c1, Class2 c2) => c2 == null ? c1.Foo() : true; + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - public static class Extensions - { - public static bool Foo(this Class1 c1) => true; - } - - public class Class2 - { } -}"; + """ + namespace A.B.C + { + public static class Extensions + { + public static bool Foo(this Class1 c1) => true; + } + + public class Class2 + { } + } + """; var expectedSourceReference = -@" -using System; -using A.B.C; - -namespace A -{ - public class Class1 - { - public bool Bar(Class1 c1, Class2 c2) => c2 == null ? c1.Foo() : true; - } -}"; + """ + using System; + using A.B.C; + + namespace A + { + public class Class1 + { + public bool Bar(Class1 c1, Class2 c2) => c2 == null ? c1.Foo() : true; + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -2057,52 +2136,53 @@ public async Task ChangeNamespace_WithExtensionMethodReferencesInVBDocument() var (folder, filePath) = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -using System; - -namespace [||]{declaredNamespace} -{{ - public static class Extensions - {{ - public static bool Foo(this String s) => true; - }} -}} - - - -Imports {declaredNamespace} - -Public Class VBClass - Public Function Foo(s As string) As Boolean - Return s.Foo() - End Function -End Class - -"; + $$""" + + + using System; + + namespace [||]{{declaredNamespace}} + { + public static class Extensions + { + public static bool Foo(this String s) => true; + } + } + + + Imports {{declaredNamespace}} + + Public Class VBClass + Public Function Foo(s As string) As Boolean + Return s.Foo() + End Function + End Class + + + """; var expectedSourceOriginal = -$@" -using System; - -namespace {defaultNamespace} -{{ - public static class Extensions - {{ - public static bool Foo(this string s) => true; - }} -}}"; + $$""" + using System; + + namespace {{defaultNamespace}} + { + public static class Extensions + { + public static bool Foo(this string s) => true; + } + } + """; var expectedSourceReference = -$@" -Imports {defaultNamespace} - -Public Class VBClass - Public Function Foo(s As string) As Boolean - Return s.Foo() - End Function -End Class"; + $""" + Imports {defaultNamespace} + + Public Class VBClass + Public Function Foo(s As string) As Boolean + Return s.Foo() + End Function + End Class + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -2115,57 +2195,59 @@ public async Task ChangeNamespace_WithMemberAccessReferencesInOtherDocument() var documentPath1 = CreateDocumentFilePath(["B", "C"], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - enum Enum1 - {{ - A, - B, - C - }} -}} - -namespace Foo -{{ - class RefClass - {{ - Enum1 M1() - {{ - return {declaredNamespace}.Enum1.A; - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + enum Enum1 + { + A, + B, + C + } + } + namespace Foo + { + class RefClass + { + Enum1 M1() + { + return {{declaredNamespace}}.Enum1.A; + } + } + } + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - enum Enum1 - { - A, - B, - C - } -}"; + """ + namespace A.B.C + { + enum Enum1 + { + A, + B, + C + } + } + """; var expectedSourceReference = -@" -using A.B.C; - -namespace Foo -{ - class RefClass - { - Enum1 M1() - { - return Enum1.A; - } - } -}"; + """ + using A.B.C; + + namespace Foo + { + class RefClass + { + Enum1 M1() + { + return Enum1.A; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -2178,52 +2260,55 @@ public async Task ChangeToGlobalNamespace_WithMemberAccessReferencesInOtherDocum var documentPath1 = CreateDocumentFilePath([], "File1.cs"); var documentPath2 = CreateDocumentFilePath([], "File2.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - enum Enum1 - {{ - A, - B, - C - }} -}} - -namespace Foo -{{ - class RefClass - {{ - Enum1 M1() - {{ - return {declaredNamespace}.Enum1.A; - }} - }} -}} - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + enum Enum1 + { + A, + B, + C + } + } + namespace Foo + { + class RefClass + { + Enum1 M1() + { + return {{declaredNamespace}}.Enum1.A; + } + } + } + + + """; var expectedSourceOriginal = -@"enum Enum1 -{ - A, - B, - C -} -"; + """ + enum Enum1 + { + A, + B, + C + } + + """; var expectedSourceReference = -@"namespace Foo -{ - class RefClass - { - Enum1 M1() - { - return Enum1.A; - } - } -}"; + """ + namespace Foo + { + class RefClass + { + Enum1 M1() + { + return Enum1.A; + } + } + } + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -2235,46 +2320,51 @@ public async Task ChangeNamespace_WithMemberAccessReferencesInVBDocument() var documentPath1 = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - public enum Enum1 - {{ - A, - B, - C - }} -}} - - - -Public Class VBClass - Sub M() - Dim x = A.B.C.D.Enum1.A - End Sub -End Class - -"; + $$""" + + + + namespace [||]{{declaredNamespace}} + { + public enum Enum1 + { + A, + B, + C + } + } + + + + Public Class VBClass + Sub M() + Dim x = A.B.C.D.Enum1.A + End Sub + End Class + + + """; var expectedSourceOriginal = -@"namespace A.B.C -{ - public enum Enum1 - { - A, - B, - C - } -}"; + """ + namespace A.B.C + { + public enum Enum1 + { + A, + B, + C + } + } + """; var expectedSourceReference = -@"Public Class VBClass - Sub M() - Dim x = A.B.C.Enum1.A - End Sub -End Class"; + """ + Public Class VBClass + Sub M() + Dim x = A.B.C.Enum1.A + End Sub + End Class + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } @@ -2286,44 +2376,47 @@ public async Task ChangeToGlobalNamespace_WithMemberAccessReferencesInVBDocument var documentPath1 = CreateDocumentFilePath([], "File1.cs"); var code = -$@" - - - -namespace [||]{declaredNamespace} -{{ - public enum Enum1 - {{ - A, - B, - C - }} -}} - - - -Public Class VBClass - Sub M() - Dim x = A.B.C.D.Enum1.A - End Sub -End Class - -"; + $$""" + + + namespace [||]{{declaredNamespace}} + { + public enum Enum1 + { + A, + B, + C + } + } + + + Public Class VBClass + Sub M() + Dim x = A.B.C.D.Enum1.A + End Sub + End Class + + + """; var expectedSourceOriginal = -@"public enum Enum1 -{ - A, - B, - C -} -"; + """ + public enum Enum1 + { + A, + B, + C + } + + """; var expectedSourceReference = -@"Public Class VBClass - Sub M() - Dim x = Enum1.A - End Sub -End Class"; + """ + Public Class VBClass + Sub M() + Dim x = Enum1.A + End Sub + End Class + """; await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference); } diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs index 9ed96d8441b55..1f5e19957748a 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AbstractCSharpCompletionProviderTests.cs @@ -62,6 +62,16 @@ private protected override Task BaseVerifyWorkerAsync( displayTextPrefix, inlineDescription, isComplexTextEdit, matchingFilters, flags, options, skipSpeculation: skipSpeculation); } + private protected override Task BaseVerifyWorkerAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, + SourceCodeKind sourceCodeKind, ItemExpectation[] expectedResults, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) + { + return base.VerifyWorkerAsync( + code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionItem, sourceCodeKind, + expectedResults, matchingFilters, flags, options, skipSpeculation); + } + private protected override async Task VerifyWorkerAsync( string code, int position, string expectedItemOrNull, string expectedDescriptionOrNull, diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs index 2c1975a5dbd0c..e848f3223fa8c 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs @@ -1892,7 +1892,7 @@ void M() } } """; - await VerifyItemExistsAsync(markup, "streamReader"); + await VerifyItemIsAbsentAsync(markup, "streamReader"); } [Fact] diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests_NameDeclarationInfoTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests_NameDeclarationInfoTests.cs index 18fdf14d35f79..b0d85e386fdc2 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests_NameDeclarationInfoTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests_NameDeclarationInfoTests.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -19,9 +17,9 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.DeclarationInfoTests; [UseExportProvider] [Trait(Traits.Feature, Traits.Features.Completion)] -public class DeclarationNameCompletion_ContextTests +public sealed class DeclarationNameCompletion_ContextTests { - protected CSharpTestWorkspaceFixture fixture = new CSharpTestWorkspaceFixture(); + private readonly CSharpTestWorkspaceFixture _fixture = new(); [Fact] public async Task AfterTypeInClass1() @@ -257,10 +255,9 @@ void M() } } """; - await VerifySymbolKinds(markup, - new SymbolKindOrTypeKind(SymbolKind.Local)); + await VerifySymbolKinds(markup); await VerifyModifiers(markup, new DeclarationModifiers()); - await VerifyTypeName(markup, "int"); + await VerifyTypeName(markup, null); await VerifyAccessibility(markup, null); } @@ -772,10 +769,10 @@ await VerifySymbolKinds(markup, new SymbolKindOrTypeKind(MethodKind.LocalFunction)); } - private async Task VerifyTypeName(string markup, string typeName) + private async Task VerifyTypeName(string markup, string? typeName) { var result = await GetResultsAsync(markup); - Assert.Equal(typeName, result.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); + Assert.Equal(typeName, result.Type?.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); } private async Task VerifyNoModifiers(string markup) @@ -812,6 +809,6 @@ private async Task GetResultsAsync(string markup) private (Document, int) ApplyChangesToFixture(string markup) { MarkupTestFile.GetPosition(markup, out var text, out int position); - return (fixture.UpdateDocument(text, SourceCodeKind.Regular), position); + return (_fixture.UpdateDocument(text, SourceCodeKind.Regular), position); } } diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index f5cdbe0e0dcb7..c98b9e085ab6b 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -253,50 +253,76 @@ class C { [Fact] public async Task OpenStringLiteral() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", AddInsideMethod("string s = \"$$")), @"String"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", AddInsideMethod("string s = \"$$")), @"System"); + var code = AddUsingDirectives("using System;", AddInsideMethod("string s = \"$$")); + await VerifyExpectedItemsAsync(code, [ + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") + ]); } [Fact] public async Task OpenStringLiteralInDirective() { - await VerifyItemIsAbsentAsync("#r \"$$", "String", expectedDescriptionOrNull: null, sourceCodeKind: SourceCodeKind.Script); - await VerifyItemIsAbsentAsync("#r \"$$", "System", expectedDescriptionOrNull: null, sourceCodeKind: SourceCodeKind.Script); + var code = "#r \"$$"; + await VerifyExpectedItemsAsync( + code, [ + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") + ], + sourceCodeKind: SourceCodeKind.Script); } [Fact] public async Task StringLiteral() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", AddInsideMethod("string s = \"$$\";")), @"System"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", AddInsideMethod("string s = \"$$\";")), @"String"); + var code = AddUsingDirectives("using System;", AddInsideMethod("string s = \"$$\";")); + await VerifyExpectedItemsAsync(code, [ + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") + ]); } [Fact] public async Task StringLiteralInDirective() { - await VerifyItemIsAbsentAsync("#r \"$$\"", "String", expectedDescriptionOrNull: null, sourceCodeKind: SourceCodeKind.Script); - await VerifyItemIsAbsentAsync("#r \"$$\"", "System", expectedDescriptionOrNull: null, sourceCodeKind: SourceCodeKind.Script); + var code = "#r \"$$\""; + await VerifyExpectedItemsAsync( + code, [ + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") + ], + sourceCodeKind: SourceCodeKind.Script); } [Fact] public async Task OpenCharLiteral() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", AddInsideMethod("char c = '$$")), @"System"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", AddInsideMethod("char c = '$$")), @"String"); + var code = AddUsingDirectives("using System;", AddInsideMethod("char c = '$$")); + await VerifyExpectedItemsAsync(code, [ + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") + ]); } [Fact] public async Task AssemblyAttribute1() { - await VerifyItemExistsAsync(@"[assembly: $$]", @"System"); - await VerifyItemIsAbsentAsync(@"[assembly: $$]", @"String"); + var code = @"[assembly: $$]"; + await VerifyExpectedItemsAsync(code, [ + ItemExpectation.Absent("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task AssemblyAttribute2() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"[assembly: $$]"), @"System"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"[assembly: $$]"), @"AttributeUsage"); + var code = @"[assembly: $$]"; + var source = AddUsingDirectives("using System;", code); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("System"), + ItemExpectation.Exists("AttributeUsage") + ]); } [Fact] @@ -321,8 +347,11 @@ class CL {}"; [Fact] public async Task TypeParamAttribute() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL<[A$$]T> {}"), @"AttributeUsage"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL<[A$$]T> {}"), @"System"); + var code = AddUsingDirectives("using System;", @"class CL<[A$$]T> {}"); + await VerifyExpectedItemsAsync(code, [ + ItemExpectation.Exists("AttributeUsage"), + ItemExpectation.Exists("System") + ]); } [Fact] @@ -332,8 +361,11 @@ public async Task MethodAttribute() [$$] void Method() {} }"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"AttributeUsage"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"System"); + var code = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(code, [ + ItemExpectation.Exists("AttributeUsage"), + ItemExpectation.Exists("System") + ]); } [Fact] @@ -342,8 +374,11 @@ public async Task MethodTypeParamAttribute() var content = @"class CL{ void Method<[A$$]T> () {} }"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"AttributeUsage"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"System"); + var code = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(code, [ + ItemExpectation.Exists("AttributeUsage"), + ItemExpectation.Exists("System") + ]); } [Fact] @@ -352,8 +387,11 @@ public async Task MethodParamAttribute() var content = @"class CL{ void Method ([$$]int i) {} }"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"AttributeUsage"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"System"); + var code = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(code, [ + ItemExpectation.Exists("AttributeUsage"), + ItemExpectation.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -383,8 +421,12 @@ public async Task NamespaceName_Unqualified_TopLevelNoPeers() namespace $$"; - await VerifyItemExistsAsync(source, "System", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "String", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + ItemExpectation.Exists("System"), + ItemExpectation.Absent("String") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -394,8 +436,12 @@ public async Task NamespaceName_Unqualified_TopLevelNoPeers_FileScopedNamespace( namespace $$;"; - await VerifyItemExistsAsync(source, "System", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "String", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + ItemExpectation.Exists("System"), + ItemExpectation.Absent("String") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -432,8 +478,12 @@ namespace B { } namespace $$ }"; - await VerifyItemIsAbsentAsync(source, "A", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemExistsAsync(source, "B", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + ItemExpectation.Absent("A"), + ItemExpectation.Exists("B") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -456,8 +506,12 @@ namespace B { } } }"; - await VerifyItemIsAbsentAsync(source, "A", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "B", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + ItemExpectation.Absent("A"), + ItemExpectation.Absent("B") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -474,8 +528,12 @@ namespace B { } } }"; - await VerifyItemIsAbsentAsync(source, "A", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemExistsAsync(source, "B", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + ItemExpectation.Absent("A"), + ItemExpectation.Exists("B") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -483,8 +541,12 @@ public async Task NamespaceName_Unqualified_InnerCompletionPosition() { var source = @"namespace Sys$$tem { }"; - await VerifyItemExistsAsync(source, "System", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "Runtime", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + ItemExpectation.Exists("System"), + ItemExpectation.Absent("Runtime") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -496,32 +558,33 @@ namespace A namespace B { namespace $$ - namespace C1 { } } - namespace B.C2 { } } namespace A.B.C3 { }"; - // Ideally, all the C* namespaces would be recommended but, because of how the parser - // recovers from the missing braces, they end up with the following qualified names... - // - // C1 => A.B.?.C1 - // C2 => A.B.B.C2 - // C3 => A.A.B.C3 - // - // ...none of which are found by the current algorithm. - await VerifyItemIsAbsentAsync(source, "C1", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "C2", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "C3", sourceCodeKind: SourceCodeKind.Regular); - - await VerifyItemIsAbsentAsync(source, "A", sourceCodeKind: SourceCodeKind.Regular); - - // Because of the above, B does end up in the completion list - // since A.B.B appears to be a peer of the new declaration - await VerifyItemExistsAsync(source, "B", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync( + source, [ + // Ideally, all the C* namespaces would be recommended but, because of how the parser + // recovers from the missing braces, they end up with the following qualified names... + // + // C1 => A.B.?.C1 + // C2 => A.B.B.C2 + // C3 => A.A.B.C3 + // + // ...none of which are found by the current algorithm. + ItemExpectation.Absent("C1"), + ItemExpectation.Absent("C2"), + ItemExpectation.Absent("C3"), + ItemExpectation.Absent("A"), + + // Because of the above, B does end up in the completion list + // since A.B.B appears to be a peer of the new declaration + ItemExpectation.Exists("B") + ], + SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -565,9 +628,13 @@ namespace B.C { } namespace B.$$ }"; - await VerifyItemIsAbsentAsync(source, "A", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "B", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemExistsAsync(source, "C", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + ItemExpectation.Absent("A"), + ItemExpectation.Absent("B"), + ItemExpectation.Exists("C") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -580,8 +647,12 @@ namespace B { } } "; - await VerifyItemIsAbsentAsync(source, "A", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "B", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + ItemExpectation.Absent("A"), + ItemExpectation.Absent("B") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -596,8 +667,12 @@ namespace B { } } "; - await VerifyItemIsAbsentAsync(source, "A", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemExistsAsync(source, "B", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + ItemExpectation.Absent("A"), + ItemExpectation.Exists("B") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -605,8 +680,12 @@ public async Task NamespaceName_Qualified_InnerCompletionPosition() { var source = @"namespace Sys$$tem.Runtime { }"; - await VerifyItemExistsAsync(source, "System", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "Runtime", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + ItemExpectation.Exists("System"), + ItemExpectation.Absent("Runtime") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -626,8 +705,12 @@ namespace System name$$space Runtime { } }"; - await VerifyItemIsAbsentAsync(source, "System", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "Runtime", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + ItemExpectation.Absent("System"), + ItemExpectation.Absent("Runtime") + ], + sourceCodeKind: SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7213")] @@ -648,39 +731,47 @@ namespace B.C.D2 { } namespace A.B.C.D3 { }"; - await VerifyItemIsAbsentAsync(source, "A", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "B", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "C", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync( + source, [ + ItemExpectation.Absent("A"), + ItemExpectation.Absent("B"), + ItemExpectation.Absent("C"), - // Ideally, all the D* namespaces would be recommended but, because of how the parser - // recovers from the missing braces, they end up with the following qualified names... - // - // D1 => A.B.C.C.?.D1 - // D2 => A.B.B.C.D2 - // D3 => A.A.B.C.D3 - // - // ...none of which are found by the current algorithm. - await VerifyItemIsAbsentAsync(source, "D1", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "D2", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(source, "D3", sourceCodeKind: SourceCodeKind.Regular); + // Ideally, all the D* namespaces would be recommended but, because of how the parser + // recovers from the missing braces, they end up with the following qualified names... + // + // D1 => A.B.C.C.?.D1 + // D2 => A.B.B.C.D2 + // D3 => A.A.B.C.D3 + // + // ...none of which are found by the current algorithm. + ItemExpectation.Absent("D1"), + ItemExpectation.Absent("D2"), + ItemExpectation.Absent("D3") + ], + SourceCodeKind.Regular); } [Fact] public async Task UnderNamespace() { - await VerifyItemIsAbsentAsync(@"namespace NS { $$", @"String"); - await VerifyItemIsAbsentAsync(@"namespace NS { $$", @"System"); + var source = @"namespace NS { $$"; + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") + ]); } [Fact] public async Task OutsideOfType1() { - await VerifyItemIsAbsentAsync(@"namespace NS { -class CL {} -$$", @"String"); - await VerifyItemIsAbsentAsync(@"namespace NS { + var source = @"namespace NS { class CL {} -$$", @"System"); +$$"; + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") + ]); } [Fact] @@ -689,14 +780,17 @@ public async Task OutsideOfType2() var content = @"namespace NS { class CL {} $$"; - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", content), @"String"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", content), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") + ]); } [Fact] public async Task CompletionInsideProperty() { - var content = @"class C + var source = @"class C { private string name; public string Name @@ -704,22 +798,30 @@ public string Name set { name = $$"; - await VerifyItemExistsAsync(content, @"value"); - await VerifyItemExistsAsync(content, @"C"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("value"), + ItemExpectation.Exists("C") + ]); } [Fact] public async Task AfterDot() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"[assembly: A.$$"), @"String"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"[assembly: A.$$"), @"System"); + var source = AddUsingDirectives("using System;", @"[assembly: A.$$"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") + ]); } [Fact] public async Task UsingAlias() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"using MyType = $$"), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"using MyType = $$"), @"System"); + var source = AddUsingDirectives("using System;", @"using MyType = $$"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("String"), + ItemExpectation.Exists("System") + ]); } [Fact] @@ -728,8 +830,11 @@ public async Task IncompleteMember() var content = @"class CL { $$ "; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] @@ -738,106 +843,151 @@ public async Task IncompleteMemberAccessibility() var content = @"class CL { public $$ "; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", content), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task BadStatement() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = $$)c")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = $$)c")), @"System"); + var source = AddUsingDirectives("using System;", AddInsideMethod(@"var t = $$)c")); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task TypeTypeParameter() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class CL<$$"), @"String"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class CL<$$"), @"System"); + var source = AddUsingDirectives("using System;", @"class CL<$$"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") + ]); } [Fact] public async Task TypeTypeParameterList() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class CL10;$$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"for(i=0;i>10;$$")), @"System"); + var content = @"for(i=0;i>10;$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task DoStatementConditionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"do {} while($$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"do {} while($$")), @"System"); + var content = @"do {} while($$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task WhileStatementConditionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"while($$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"while($$")), @"System"); + var content = @"while($$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task ArrayRankSpecifierSizesPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"int [$$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"int [$$")), @"System"); + var content = @"int [$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task PrefixUnaryExpression() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"+$$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"+$$")), @"System"); + var content = @"+$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task PostfixUnaryExpression() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"$$++")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"$$++")), @"System"); + var content = @"$$++"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task BinaryExpressionLeftPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"$$ + 1")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"$$ + 1")), @"System"); + var content = @"$$ + 1"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task BinaryExpressionRightPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"1 + $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"1 + $$")), @"System"); + var content = @"1 + $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task AssignmentExpressionLeftPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"$$ = 1")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"$$ = 1")), @"System"); + var content = @"$$ = 1"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task AssignmentExpressionRightPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"1 = $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"1 = $$")), @"System"); + var content = @"1 = $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task ConditionalExpressionConditionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"$$? 1:")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"$$? 1:")), @"System"); + var content = @"$$? 1:"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task ConditionalExpressionWhenTruePart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"true? $$:")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"true? $$:")), @"System"); + var content = @"true? $$:"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task ConditionalExpressionWhenFalsePart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"true? 1:$$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"true? 1:$$")), @"System"); + var content = @"true? 1:$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task JoinClauseInExpressionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C join p in $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C join p in $$")), @"System"); + var content = @"var t = from c in C join p in $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task JoinClauseLeftExpressionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C join p in P on $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C join p in P on $$")), @"System"); + var content = @"var t = from c in C join p in P on $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task JoinClauseRightExpressionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C join p in P on id equals $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C join p in P on id equals $$")), @"System"); + var content = @"var t = from c in C join p in P on id equals $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task WhereClauseConditionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C where $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C where $$")), @"System"); + var content = @"var t = from c in C where $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task GroupClauseGroupExpressionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C group $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C group $$")), @"System"); + var content = @"var t = from c in C group $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task GroupClauseByExpressionPart() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C group g by $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = from c in C group g by $$")), @"System"); + var content = @"var t = from c in C group g by $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task IfStatement() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"if ($$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"if ($$")), @"System"); + var content = @"if ($$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task SwitchStatement() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"switch($$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"switch($$")), @"System"); + var content = @"switch($$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task SwitchLabelCase() { var content = @"switch(i) { case $$"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task SwitchPatternLabelCase() { var content = @"switch(i) { case $$ when"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33915")] public async Task SwitchExpressionFirstBranch() { var content = @"i switch { $$"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33915")] public async Task SwitchExpressionSecondBranch() { var content = @"i switch { 1 => true, $$"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33915")] public async Task PositionalPatternFirstPosition() { var content = @"i is ($$"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33915")] public async Task PositionalPatternSecondPosition() { var content = @"i is (1, $$"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33915")] public async Task PropertyPatternFirstPosition() { var content = @"i is { P: $$"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33915")] public async Task PropertyPatternSecondPosition() { var content = @"i is { P1: 1, P2: $$"; - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(content)), @"System"); + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task InitializerExpression() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = new [] { $$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"var t = new [] { $$")), @"System"); + var content = @"var t = new [] { $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/30784")] @@ -1362,8 +1742,12 @@ class CL where T : $$", @"Test"); [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/30784")] public async Task TypeParameterConstraintClause_StillShowStaticAndSealedTypesNotDirectlyInConstraint() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL where T : IList<$$"), @"System"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL where T : IList<$$"), @"String"); + var content = @"class CL where T : IList<$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/30784")] @@ -1411,15 +1795,23 @@ class CL where T : A, $$", @"Test"); [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/30784")] public async Task TypeParameterConstraintClauseList_StillShowStaticAndSealedTypesNotDirectlyInConstraint() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL where T : A, IList<$$"), @"System"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL where T : A, IList<$$"), @"String"); + var content = @"class CL where T : A, IList<$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task TypeParameterConstraintClauseAnotherWhere() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class CL where T : A where$$"), @"System"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class CL where T : A where$$"), @"String"); + var content = @"class CL where T : A where$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") + ]); } [Fact] @@ -1447,29 +1839,45 @@ public async Task TypeSymbolOfTypeParameterConstraintClause3() [Fact] public async Task BaseList1() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL : $$"), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL : $$"), @"System"); + var content = @"class CL : $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task BaseList2() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL : B, $$"), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", @"class CL : B, $$"), @"System"); + var content = @"class CL : B, $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] public async Task BaseListWhere() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class CL : B where$$"), @"String"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class CL : B where$$"), @"System"); + var content = @"class CL : B where$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") + ]); } [Fact] public async Task AliasedName() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", AddInsideMethod(@"global::$$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"global::$$")), @"System"); + var content = @"global::$$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("String"), + ItemExpectation.Exists("System") + ]); } [Fact] @@ -1483,15 +1891,23 @@ public async Task AliasedType() [Fact] public async Task ConstructorInitializer() { - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class C { C() : $$"), @"String"); - await VerifyItemIsAbsentAsync(AddUsingDirectives("using System;", @"class C { C() : $$"), @"System"); + var content = @"class C { C() : $$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("String"), + ItemExpectation.Absent("System") + ]); } [Fact] public async Task Typeof1() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"typeof($$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"typeof($$")), @"System"); + var content = @"typeof($$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] @@ -1501,8 +1917,12 @@ public async Task Typeof2() [Fact] public async Task Sizeof1() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"sizeof($$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"sizeof($$")), @"System"); + var content = @"sizeof($$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] @@ -1512,8 +1932,12 @@ public async Task Sizeof2() [Fact] public async Task Default1() { - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"default($$")), @"String"); - await VerifyItemExistsAsync(AddUsingDirectives("using System;", AddInsideMethod(@"default($$")), @"System"); + var content = @"default($$"; + var source = AddUsingDirectives("using System;", content); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("System") + ]); } [Fact] @@ -1742,8 +2166,10 @@ void Bar() } } "; - await VerifyItemIsAbsentAsync(markup, "Hidden"); - await VerifyItemExistsAsync(markup, "Goo"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("Hidden"), + ItemExpectation.Exists("Goo") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539812")] @@ -1763,8 +2189,10 @@ void Bar() } } "; - await VerifyItemIsAbsentAsync(markup, "Hidden"); - await VerifyItemExistsAsync(markup, "Goo"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("Hidden"), + ItemExpectation.Exists("Goo") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539812")] @@ -1784,9 +2212,11 @@ void Bar() } } "; - await VerifyItemIsAbsentAsync(markup, "Hidden"); - await VerifyItemExistsAsync(markup, "Goo"); - await VerifyItemIsAbsentAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("Hidden"), + ItemExpectation.Exists("Goo"), + ItemExpectation.Absent("Bar"), + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539812")] @@ -1806,8 +2236,10 @@ void Bar() } } "; - await VerifyItemIsAbsentAsync(markup, "Hidden"); - await VerifyItemExistsAsync(markup, "Goo"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("Hidden"), + ItemExpectation.Exists("Goo") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539812")] @@ -1827,8 +2259,10 @@ void Bar() } } "; - await VerifyItemIsAbsentAsync(markup, "Hidden"); - await VerifyItemExistsAsync(markup, "Goo"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("Hidden"), + ItemExpectation.Exists("Goo") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539812")] @@ -1848,8 +2282,10 @@ void Bar() } } "; - await VerifyItemIsAbsentAsync(markup, "Hidden"); - await VerifyItemExistsAsync(markup, "Goo"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("Hidden"), + ItemExpectation.Exists("Goo") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539812")] @@ -1872,10 +2308,12 @@ void Bar() } } "; - await VerifyItemIsAbsentAsync(markup, "HiddenStatic"); - await VerifyItemExistsAsync(markup, "GooStatic"); - await VerifyItemIsAbsentAsync(markup, "HiddenInstance"); - await VerifyItemExistsAsync(markup, "GooInstance"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("HiddenStatic"), + ItemExpectation.Exists("GooStatic"), + ItemExpectation.Absent("HiddenInstance"), + ItemExpectation.Exists("GooInstance") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540155")] @@ -2043,8 +2481,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540212")] @@ -2060,8 +2500,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/24326")] @@ -2077,8 +2519,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") + ]); } [Fact] @@ -2094,8 +2538,10 @@ void M(ref $$) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "field"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Absent("field") + ]); } [Fact] @@ -2111,8 +2557,10 @@ void M(out $$) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "field"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Absent("field") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/24326")] @@ -2128,8 +2576,10 @@ void M(in $$) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "field"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Absent("field") + ]); } [Fact] @@ -2145,8 +2595,10 @@ void M(ref String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemExistsAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("parameter") + ]); } [Fact] @@ -2162,8 +2614,10 @@ void M(out String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemExistsAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("parameter") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/24326")] @@ -2179,8 +2633,10 @@ void M(in String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemExistsAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("parameter") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25569")] @@ -2196,8 +2652,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemExistsAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Exists("parameter") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25569")] @@ -2213,8 +2671,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25569")] @@ -2230,8 +2690,10 @@ ref readonly $$ } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") + ]); } [Fact] @@ -2247,8 +2709,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") + ]); } [Fact] @@ -2264,8 +2728,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") + ]); } [Fact] @@ -2281,8 +2747,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") + ]); } [Fact] @@ -2298,8 +2766,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35178")] @@ -2336,10 +2806,12 @@ void M() } } "; - await VerifyItemExistsAsync(markup, "ToString"); - await VerifyItemIsAbsentAsync(markup, "GetType"); - await VerifyItemIsAbsentAsync(markup, "Equals"); - await VerifyItemIsAbsentAsync(markup, "GetHashCode"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("ToString"), + ItemExpectation.Absent("GetType"), + ItemExpectation.Absent("Equals"), + ItemExpectation.Absent("GetHashCode") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35178")] @@ -2356,10 +2828,12 @@ void M() } } "; - await VerifyItemExistsAsync(markup, "ToString"); - await VerifyItemExistsAsync(markup, "GetType"); - await VerifyItemExistsAsync(markup, "Equals"); - await VerifyItemExistsAsync(markup, "GetHashCode"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("ToString"), + ItemExpectation.Exists("GetType"), + ItemExpectation.Exists("Equals"), + ItemExpectation.Exists("GetHashCode") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/53585")] @@ -2375,8 +2849,10 @@ void M(String parameter) } } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") + ]); } [Theory] @@ -2401,8 +2877,10 @@ void M(String parameter) }} }} "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Absent("parameter") + ]); } [Theory] @@ -2425,8 +2903,10 @@ void M(String parameter) }} }} "; - await VerifyItemIsAbsentAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("String"), + ItemExpectation.Absent("parameter") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/60341")] @@ -2442,8 +2922,10 @@ async async $$ } } "; - await VerifyItemIsAbsentAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("String"), + ItemExpectation.Absent("parameter") + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/53585")] @@ -2466,8 +2948,10 @@ void M(String parameter) }} }} "; - await VerifyItemIsAbsentAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "parameter"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("String"), + ItemExpectation.Absent("parameter") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25569")] @@ -2481,8 +2965,10 @@ class C ref $$ } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "field"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Absent("field") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25569")] @@ -2496,8 +2982,10 @@ class C ref readonly $$ } "; - await VerifyItemExistsAsync(markup, "String"); - await VerifyItemIsAbsentAsync(markup, "field"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("String"), + ItemExpectation.Absent("field") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539217")] @@ -2513,8 +3001,10 @@ class R } } "; - await VerifyItemExistsAsync(markup, "Q"); - await VerifyItemExistsAsync(markup, "R"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Q"), + ItemExpectation.Exists("R") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539217")] @@ -2529,8 +3019,10 @@ class R } } "; - await VerifyItemExistsAsync(markup, "Q"); - await VerifyItemExistsAsync(markup, "R"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Q"), + ItemExpectation.Exists("R") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539217")] @@ -2545,8 +3037,10 @@ class R $$ } "; - await VerifyItemExistsAsync(markup, "Q"); - await VerifyItemExistsAsync(markup, "R"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Q"), + ItemExpectation.Exists("R") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539217")] @@ -2596,8 +3090,10 @@ class R { } $$"; // At EOF - await VerifyItemExistsAsync(markup, "Q"); - await VerifyItemExistsAsync(markup, "R"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Q"), + ItemExpectation.Exists("R") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539217")] @@ -2609,8 +3105,10 @@ class Q class R { $$"; // At EOF - await VerifyItemExistsAsync(markup, "Q"); - await VerifyItemExistsAsync(markup, "R"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Q"), + ItemExpectation.Exists("R") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540574")] @@ -2740,8 +3238,10 @@ public void M() } "; - await VerifyItemExistsAsync(markup, "ToString"); - await VerifyItemExistsAsync(markup, "Invoke"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("ToString"), + ItemExpectation.Exists("Invoke") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541216")] @@ -2865,8 +3365,10 @@ public async Task AttributeName() using System; [$$"; - await VerifyItemExistsAsync(markup, "CLSCompliant"); - await VerifyItemIsAbsentAsync(markup, "CLSCompliantAttribute"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("CLSCompliant"), + ItemExpectation.Absent("CLSCompliantAttribute") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542225")] @@ -2877,8 +3379,10 @@ public async Task AttributeNameAfterSpecifier() [assembly:$$ "; - await VerifyItemExistsAsync(markup, "CLSCompliant"); - await VerifyItemIsAbsentAsync(markup, "CLSCompliantAttribute"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("CLSCompliant"), + ItemExpectation.Absent("CLSCompliantAttribute") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542225")] @@ -2888,8 +3392,10 @@ public async Task AttributeNameInAttributeList() using System; [CLSCompliant, $$"; - await VerifyItemExistsAsync(markup, "CLSCompliant"); - await VerifyItemIsAbsentAsync(markup, "CLSCompliantAttribute"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("CLSCompliant"), + ItemExpectation.Absent("CLSCompliantAttribute") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542225")] @@ -2900,8 +3406,10 @@ public async Task AttributeNameBeforeClass() [$$ class C { }"; - await VerifyItemExistsAsync(markup, "CLSCompliant"); - await VerifyItemIsAbsentAsync(markup, "CLSCompliantAttribute"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("CLSCompliant"), + ItemExpectation.Absent("CLSCompliantAttribute") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542225")] @@ -2912,8 +3420,10 @@ public async Task AttributeNameAfterSpecifierBeforeClass() [assembly:$$ class C { }"; - await VerifyItemExistsAsync(markup, "CLSCompliant"); - await VerifyItemIsAbsentAsync(markup, "CLSCompliantAttribute"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("CLSCompliant"), + ItemExpectation.Absent("CLSCompliantAttribute") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542225")] @@ -2924,8 +3434,10 @@ public async Task AttributeNameInAttributeArgumentList() [CLSCompliant($$ class C { }"; - await VerifyItemExistsAsync(markup, "CLSCompliantAttribute"); - await VerifyItemIsAbsentAsync(markup, "CLSCompliant"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("CLSCompliantAttribute"), + ItemExpectation.Absent("CLSCompliant") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542225")] @@ -2935,8 +3447,10 @@ public async Task AttributeNameInsideClass() using System; class C { $$ }"; - await VerifyItemExistsAsync(markup, "CLSCompliantAttribute"); - await VerifyItemIsAbsentAsync(markup, "CLSCompliant"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("CLSCompliantAttribute"), + ItemExpectation.Absent("CLSCompliant") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542954")] @@ -2989,8 +3503,10 @@ class MyAttribute : System.Attribute { } [Test.$$ class Program { } }"; - await VerifyItemExistsAsync(markup, "My"); - await VerifyItemIsAbsentAsync(markup, "MyAttribute"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("My"), + ItemExpectation.Absent("MyAttribute") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545121")] @@ -3006,8 +3522,10 @@ class MyAttribute : System.Attribute { } class Program { } } }"; - await VerifyItemExistsAsync(markup, "My"); - await VerifyItemIsAbsentAsync(markup, "MyAttribute"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("My"), + ItemExpectation.Absent("MyAttribute") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545121")] @@ -3020,9 +3538,11 @@ class namespaceAttribute : System.Attribute { } [$$ class Program { } }"; - await VerifyItemExistsAsync(markup, "namespaceAttribute"); - await VerifyItemIsAbsentAsync(markup, "namespace"); - await VerifyItemIsAbsentAsync(markup, "@namespace"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("namespaceAttribute"), + ItemExpectation.Absent("namespace"), + ItemExpectation.Absent("@namespace") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545121")] @@ -3035,9 +3555,11 @@ class namespaceAttribute : System.Attribute { } [Test.$$ class Program { } }"; - await VerifyItemExistsAsync(markup, "namespaceAttribute"); - await VerifyItemIsAbsentAsync(markup, "namespace"); - await VerifyItemIsAbsentAsync(markup, "@namespace"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("namespaceAttribute"), + ItemExpectation.Absent("namespace"), + ItemExpectation.Absent("@namespace") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545348")] @@ -3055,17 +3577,19 @@ void M() } }"; - // preprocessor keyword - await VerifyItemExistsAsync(markup, "error"); - await VerifyItemIsAbsentAsync(markup, "@error"); + await VerifyExpectedItemsAsync(markup, [ + // preprocessor keyword + ItemExpectation.Exists("error"), + ItemExpectation.Absent("@error"), - // contextual keyword - await VerifyItemExistsAsync(markup, "method"); - await VerifyItemIsAbsentAsync(markup, "@method"); + // contextual keyword + ItemExpectation.Exists("method"), + ItemExpectation.Absent("@method"), - // full keyword - await VerifyItemExistsAsync(markup, "@int"); - await VerifyItemIsAbsentAsync(markup, "int"); + // full keyword + ItemExpectation.Exists("@int"), + ItemExpectation.Absent("int") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545348")] @@ -3081,8 +3605,10 @@ void M() } }"; - await VerifyItemExistsAsync(markup, "@from"); - await VerifyItemIsAbsentAsync(markup, "from"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("@from"), + ItemExpectation.Absent("from") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545348")] @@ -3100,10 +3626,12 @@ void M() } }"; - await VerifyItemExistsAsync(markup, "@from"); - await VerifyItemIsAbsentAsync(markup, "from"); - await VerifyItemExistsAsync(markup, "@where"); - await VerifyItemIsAbsentAsync(markup, "where"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("@from"), + ItemExpectation.Absent("from"), + ItemExpectation.Exists("@where"), + ItemExpectation.Absent("where") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545348")] @@ -3121,10 +3649,12 @@ void M() } }"; - await VerifyItemExistsAsync(markup, "@from"); - await VerifyItemIsAbsentAsync(markup, "from"); - await VerifyItemExistsAsync(markup, "@where"); - await VerifyItemIsAbsentAsync(markup, "where"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("@from"), + ItemExpectation.Absent("from"), + ItemExpectation.Exists("@where"), + ItemExpectation.Absent("where") + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545121")] @@ -3134,8 +3664,12 @@ public async Task AttributeNameAfterGlobalAlias() class MyAttribute : System.Attribute { } [global::$$ class Program { }"; - await VerifyItemExistsAsync(markup, "My", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(markup, "MyAttribute", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync( + markup, [ + ItemExpectation.Exists("My"), + ItemExpectation.Absent("MyAttribute") + ], + SourceCodeKind.Regular); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545121")] @@ -3145,9 +3679,13 @@ public async Task AttributeNameAfterGlobalAliasWhenSuffixlessFormIsKeyword() class namespaceAttribute : System.Attribute { } [global::$$ class Program { }"; - await VerifyItemExistsAsync(markup, "namespaceAttribute", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(markup, "namespace", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemIsAbsentAsync(markup, "@namespace", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync( + markup, [ + ItemExpectation.Exists("namespaceAttribute"), + ItemExpectation.Absent("namespace"), + ItemExpectation.Absent("@namespace") + ], + SourceCodeKind.Regular); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25589")] @@ -3175,8 +3713,10 @@ namespace Namespace3.Namespace4 { class CustomAttribute : System.Attribute { } } } [Namespace1.$$]"; - await VerifyItemIsAbsentAsync(markup, "Namespace2"); - await VerifyItemExistsAsync(markup, "Namespace3"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("Namespace2"), + ItemExpectation.Exists("Namespace3"), + ]); } [Fact] @@ -3223,10 +3763,12 @@ namespace Namespace3.Namespace4 { class CustomAttribute : System.Attribute { } } } [$$]"; - await VerifyItemExistsAsync(markup, "Namespace1Alias"); - await VerifyItemIsAbsentAsync(markup, "Namespace2Alias"); - await VerifyItemExistsAsync(markup, "Namespace3Alias"); - await VerifyItemExistsAsync(markup, "Namespace4Alias"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Namespace1Alias"), + ItemExpectation.Absent("Namespace2Alias"), + ItemExpectation.Exists("Namespace3Alias"), + ItemExpectation.Exists("Namespace4Alias"), + ]); } [Fact] @@ -3692,10 +4234,12 @@ void M() } "; - await VerifyItemExistsAsync(markup, "a"); - await VerifyItemExistsAsync(markup, "b"); - await VerifyItemExistsAsync(markup, "c"); - await VerifyItemIsAbsentAsync(markup, "Equals"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("a"), + ItemExpectation.Exists("b"), + ItemExpectation.Exists("c"), + ItemExpectation.Absent("Equals"), + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543104")] @@ -3713,10 +4257,12 @@ void M() } "; - await VerifyItemIsAbsentAsync(markup, "a"); - await VerifyItemIsAbsentAsync(markup, "b"); - await VerifyItemIsAbsentAsync(markup, "c"); - await VerifyItemExistsAsync(markup, "Equals"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("a"), + ItemExpectation.Absent("b"), + ItemExpectation.Absent("c"), + ItemExpectation.Exists("Equals"), + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529138")] @@ -7029,9 +7575,10 @@ static void Main() A.$$ } }"; - - await VerifyItemExistsAsync(markup, "Goo"); - await VerifyItemExistsAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Goo"), + ItemExpectation.Exists("Bar"), + ]); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545647")] @@ -7215,8 +7762,10 @@ class Program public async $$ }"; - await VerifyItemExistsAsync(markup, "Task"); - await VerifyItemIsAbsentAsync(markup, "Console"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Task"), + ItemExpectation.Absent("Console"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/60341")] @@ -7231,8 +7780,10 @@ public async $$ class Test {}"; - await VerifyItemExistsAsync(markup, "Task"); - await VerifyItemIsAbsentAsync(markup, "Test"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Task"), + ItemExpectation.Absent("Test"), + ]); } [Fact] @@ -7371,8 +7922,10 @@ class Request } }"; // Nothing should be found: no awaiter for request. - await VerifyItemIsAbsentAsync(markup, "Result"); - await VerifyItemIsAbsentAsync(markup, "ReadAsStreamAsync"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("Result"), + ItemExpectation.Absent("ReadAsStreamAsync"), + ]); } [Fact] @@ -7399,8 +7952,10 @@ class Request } }"; - await VerifyItemIsAbsentAsync(markup, "Result"); - await VerifyItemExistsAsync(markup, "ReadAsStreamAsync"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("Result"), + ItemExpectation.Exists("ReadAsStreamAsync"), + ]); } [Fact] @@ -8216,8 +8771,10 @@ public void goo() var q = a?.$$AB.BA.AB.BA; } }"; - await VerifyItemExistsAsync(markup, "AA"); - await VerifyItemExistsAsync(markup, "AB"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("AA"), + ItemExpectation.Exists("AB"), + ]); } [Fact] @@ -8239,8 +8796,10 @@ public void goo() var q = a?.s?.$$; } }"; - await VerifyItemExistsAsync(markup, "i"); - await VerifyItemIsAbsentAsync(markup, "value"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("i"), + ItemExpectation.Absent("Value"), + ]); } [Fact] @@ -8261,8 +8820,10 @@ public void goo() var q = s?.$$i?.ToString(); } }"; - await VerifyItemExistsAsync(markup, "i"); - await VerifyItemIsAbsentAsync(markup, "value"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("i"), + ItemExpectation.Absent("Value"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/54361")] @@ -8277,8 +8838,10 @@ void M(System.DateTime? dt) } } "; - await VerifyItemExistsAsync(markup, "Day"); - await VerifyItemIsAbsentAsync(markup, "Value"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Day"), + ItemExpectation.Absent("Value"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/54361")] @@ -8293,8 +8856,10 @@ void M(System.DateTime? dt) } } "; - await VerifyItemExistsAsync(markup, "Value"); - await VerifyItemIsAbsentAsync(markup, "Day"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Value"), + ItemExpectation.Absent("Day"), + ]); } [Fact] @@ -8707,8 +9272,10 @@ void goo() } } "; - await VerifyItemExistsAsync(markup, "x"); - await VerifyItemExistsAsync(markup, "y"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("x"), + ItemExpectation.Exists("y"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1663")] @@ -8938,9 +9505,11 @@ static class D { } namespace M { } }"; - await VerifyItemIsAbsentAsync(markup, "A"); - await VerifyItemIsAbsentAsync(markup, "B"); - await VerifyItemExistsAsync(markup, "N"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("A"), + ItemExpectation.Absent("B"), + ItemExpectation.Exists("N"), + ]); } [Fact] @@ -8960,9 +9529,11 @@ static class D { } namespace M { } }"; - await VerifyItemIsAbsentAsync(markup, "C"); - await VerifyItemIsAbsentAsync(markup, "D"); - await VerifyItemExistsAsync(markup, "M"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("C"), + ItemExpectation.Absent("D"), + ItemExpectation.Exists("M"), + ]); } [Fact] @@ -8982,9 +9553,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "A"); - await VerifyItemExistsAsync(markup, "B"); - await VerifyItemExistsAsync(markup, "N"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("N"), + ]); } [Fact] @@ -9004,9 +9577,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "C"); - await VerifyItemExistsAsync(markup, "D"); - await VerifyItemExistsAsync(markup, "M"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("C"), + ItemExpectation.Exists("D"), + ItemExpectation.Exists("M"), + ]); } [Fact] @@ -9026,9 +9601,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "A"); - await VerifyItemExistsAsync(markup, "B"); - await VerifyItemExistsAsync(markup, "N"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("N"), + ]); } [Fact] @@ -9048,9 +9625,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "C"); - await VerifyItemExistsAsync(markup, "D"); - await VerifyItemExistsAsync(markup, "M"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("C"), + ItemExpectation.Exists("D"), + ItemExpectation.Exists("M"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67985")] @@ -9070,9 +9649,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "A"); - await VerifyItemExistsAsync(markup, "B"); - await VerifyItemExistsAsync(markup, "N"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("N"), + ]); } [Fact] @@ -9092,9 +9673,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "A"); - await VerifyItemIsAbsentAsync(markup, "B"); - await VerifyItemExistsAsync(markup, "N"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("A"), + ItemExpectation.Absent("B"), + ItemExpectation.Exists("N"), + ]); } [Fact] @@ -9114,9 +9697,11 @@ class C { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "C"); - await VerifyItemIsAbsentAsync(markup, "D"); - await VerifyItemExistsAsync(markup, "M"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("C"), + ItemExpectation.Absent("D"), + ItemExpectation.Exists("M"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67985")] @@ -9136,9 +9721,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "A"); - await VerifyItemIsAbsentAsync(markup, "B"); - await VerifyItemExistsAsync(markup, "N"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("A"), + ItemExpectation.Absent("B"), + ItemExpectation.Exists("N"), + ]); } [Fact] @@ -9159,9 +9746,11 @@ interface I { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "C"); - await VerifyItemExistsAsync(markup, "I"); - await VerifyItemExistsAsync(markup, "M"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("C"), + ItemExpectation.Exists("I"), + ItemExpectation.Exists("M"), + ]); } [Fact] @@ -9182,9 +9771,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "A"); - await VerifyItemExistsAsync(markup, "I"); - await VerifyItemExistsAsync(markup, "N"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("A"), + ItemExpectation.Exists("I"), + ItemExpectation.Exists("N"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67985")] @@ -9205,9 +9796,11 @@ static class D { } namespace M { } }"; - await VerifyItemExistsAsync(markup, "A"); - await VerifyItemExistsAsync(markup, "I"); - await VerifyItemExistsAsync(markup, "N"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("A"), + ItemExpectation.Exists("I"), + ItemExpectation.Exists("N"), + ]); } [Fact] @@ -9236,8 +9829,10 @@ void M() } "; - await VerifyItemIsAbsentAsync(markup, "Goo"); - await VerifyItemIsAbsentAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("Goo"), + ItemExpectation.Absent("Bar"), + ]); } [Fact] @@ -9268,8 +9863,10 @@ void M() } "; - await VerifyItemIsAbsentAsync(markup, "Goo"); - await VerifyItemIsAbsentAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("Goo"), + ItemExpectation.Absent("Bar"), + ]); } [Fact] @@ -9301,8 +9898,10 @@ void M() } "; - await VerifyItemExistsAsync(markup, "Goo"); - await VerifyItemExistsAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Goo"), + ItemExpectation.Exists("Bar"), + ]); } [Fact] @@ -9335,8 +9934,10 @@ void M() } "; - await VerifyItemExistsAsync(markup, "Goo"); - await VerifyItemExistsAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Goo"), + ItemExpectation.Exists("Bar"), + ]); } [Fact] @@ -9368,8 +9969,10 @@ void M() } "; - await VerifyItemExistsAsync(markup, "Goo"); - await VerifyItemIsAbsentAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Goo"), + ItemExpectation.Absent("Bar"), + ]); } [Fact] @@ -9401,8 +10004,10 @@ void M() } "; - await VerifyItemIsAbsentAsync(markup, "Goo"); - await VerifyItemExistsAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("Goo"), + ItemExpectation.Exists("Bar"), + ]); } [Fact] @@ -9435,8 +10040,10 @@ void M() } "; - await VerifyItemExistsAsync(markup, "Goo"); - await VerifyItemExistsAsync(markup, "Bar"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Goo"), + ItemExpectation.Exists("Bar"), + ]); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/7932")] @@ -9831,25 +10438,27 @@ void goo() } }" + TestResources.NetFX.ValueTuple.tuplelib_cs; - await VerifyItemExistsAsync(markup, "Alice"); - await VerifyItemExistsAsync(markup, "Bob"); - await VerifyItemExistsAsync(markup, "CompareTo"); - await VerifyItemExistsAsync(markup, "Equals"); - await VerifyItemExistsAsync(markup, "GetHashCode"); - await VerifyItemExistsAsync(markup, "GetType"); - await VerifyItemExistsAsync(markup, "Item2"); - await VerifyItemExistsAsync(markup, "ITEM3"); - for (var i = 4; i <= 8; i++) - { - await VerifyItemExistsAsync(markup, "Item" + i); - } - - await VerifyItemExistsAsync(markup, "ToString"); - - await VerifyItemIsAbsentAsync(markup, "Item1"); - await VerifyItemIsAbsentAsync(markup, "Item9"); - await VerifyItemIsAbsentAsync(markup, "Rest"); - await VerifyItemIsAbsentAsync(markup, "Item3"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Alice"), + ItemExpectation.Exists("Bob"), + ItemExpectation.Exists("CompareTo"), + ItemExpectation.Exists("Equals"), + ItemExpectation.Exists("GetHashCode"), + ItemExpectation.Exists("GetType"), + ItemExpectation.Exists("Item2"), + ItemExpectation.Exists("ITEM3"), + ItemExpectation.Exists("Item4"), + ItemExpectation.Exists("Item5"), + ItemExpectation.Exists("Item6"), + ItemExpectation.Exists("Item7"), + ItemExpectation.Exists("Item8"), + ItemExpectation.Exists("ToString"), + + ItemExpectation.Absent("Item1"), + ItemExpectation.Absent("Item9"), + ItemExpectation.Absent("Rest"), + ItemExpectation.Absent("Item3") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/14546")] @@ -10412,9 +11021,11 @@ void M(SomeCollection c) } }"; - await VerifyItemIsAbsentAsync(markup, "Substring"); - await VerifyItemExistsAsync(markup, "A"); - await VerifyItemExistsAsync(markup, "B"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("Substring"), + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ]); } [Fact, WorkItem("https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1056325")] @@ -10523,8 +11134,10 @@ static void Create(Dictionary list, Action expression) { } class Product1 { public void MyProperty1() { } } class Product2 { public void MyProperty2() { } }"; - await VerifyItemExistsAsync(markup, "MyProperty1"); - await VerifyItemExistsAsync(markup, "MyProperty2"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("MyProperty1"), + ItemExpectation.Exists("MyProperty2"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42997")] @@ -10550,9 +11163,11 @@ class Product1 { public void MyProperty1() { } } class Product2 { public void MyProperty2() { } } class Product3 { public void MyProperty3() { } }"; - await VerifyItemExistsAsync(markup, "MyProperty1"); - await VerifyItemExistsAsync(markup, "MyProperty2"); - await VerifyItemExistsAsync(markup, "MyProperty3"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("MyProperty1"), + ItemExpectation.Exists("MyProperty2"), + ItemExpectation.Exists("MyProperty3") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42997")] @@ -10728,11 +11343,13 @@ class Builder public int Something { get; set; } }"; - await VerifyItemExistsAsync(markup, "Something"); - await VerifyItemIsAbsentAsync(markup, "BeginInvoke"); - await VerifyItemIsAbsentAsync(markup, "Clone"); - await VerifyItemIsAbsentAsync(markup, "Method"); - await VerifyItemIsAbsentAsync(markup, "Target"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("Something"), + ItemExpectation.Absent("BeginInvoke"), + ItemExpectation.Absent("Clone"), + ItemExpectation.Absent("Method"), + ItemExpectation.Absent("Target") + ]); } [Fact] @@ -10755,19 +11372,21 @@ public void Test() } }"; - // Guid - await VerifyItemExistsAsync(markup, "ToByteArray"); + await VerifyExpectedItemsAsync(markup, [ + // Guid + ItemExpectation.Exists("ToByteArray"), - // Uri - await VerifyItemExistsAsync(markup, "AbsoluteUri"); - await VerifyItemExistsAsync(markup, "Fragment"); - await VerifyItemExistsAsync(markup, "Query"); + // Uri + ItemExpectation.Exists("AbsoluteUri"), + ItemExpectation.Exists("Fragment"), + ItemExpectation.Exists("Query"), - // Should not appear for Delegate - await VerifyItemIsAbsentAsync(markup, "BeginInvoke"); - await VerifyItemIsAbsentAsync(markup, "Clone"); - await VerifyItemIsAbsentAsync(markup, "Method"); - await VerifyItemIsAbsentAsync(markup, "Target"); + // Should not appear for Delegate + ItemExpectation.Absent("BeginInvoke"), + ItemExpectation.Absent("Clone"), + ItemExpectation.Absent("Method"), + ItemExpectation.Absent("Target") + ]); } [Fact] @@ -10789,20 +11408,21 @@ public void Test() M(d => d.$$) } }"; + await VerifyExpectedItemsAsync(markup, [ + // Guid + ItemExpectation.Exists("ToByteArray"), - // Guid - await VerifyItemExistsAsync(markup, "ToByteArray"); - - // Should not appear for Uri - await VerifyItemIsAbsentAsync(markup, "AbsoluteUri"); - await VerifyItemIsAbsentAsync(markup, "Fragment"); - await VerifyItemIsAbsentAsync(markup, "Query"); + // Should not appear for Uri + ItemExpectation.Absent("AbsoluteUri"), + ItemExpectation.Absent("Fragment"), + ItemExpectation.Absent("Query"), - // Should not appear for Delegate - await VerifyItemIsAbsentAsync(markup, "BeginInvoke"); - await VerifyItemIsAbsentAsync(markup, "Clone"); - await VerifyItemIsAbsentAsync(markup, "Method"); - await VerifyItemIsAbsentAsync(markup, "Target"); + // Should not appear for Delegate + ItemExpectation.Absent("BeginInvoke"), + ItemExpectation.Absent("Clone"), + ItemExpectation.Absent("Method"), + ItemExpectation.Absent("Target") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36029")] @@ -10834,9 +11454,11 @@ class AnotherBuilder public int AnotherSomething { get; set; } }"; - await VerifyItemIsAbsentAsync(markup, "AnotherSomething"); - await VerifyItemIsAbsentAsync(markup, "FirstOrDefault"); - await VerifyItemExistsAsync(markup, "Something"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("AnotherSomething"), + ItemExpectation.Absent("FirstOrDefault"), + ItemExpectation.Exists("Something") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36029")] @@ -10865,9 +11487,11 @@ class AnotherBuilder public int AnotherSomething { get; set; } }"; - await VerifyItemIsAbsentAsync(markup, "Something"); - await VerifyItemIsAbsentAsync(markup, "FirstOrDefault"); - await VerifyItemExistsAsync(markup, "AnotherSomething"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Absent("Something"), + ItemExpectation.Absent("FirstOrDefault"), + ItemExpectation.Exists("AnotherSomething") + ]); } [Fact, Trait(Traits.Feature, Traits.Features.TargetTypedCompletion)] @@ -10971,9 +11595,14 @@ public void M(string s) } }"; - await VerifyItemExistsAsync(markup, "M"); - await VerifyItemExistsAsync(markup, "Equals"); - await VerifyItemIsAbsentAsync(markup, "DoSomething", displayTextSuffix: "<>"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("M"), + ItemExpectation.Exists("Equals"), + ItemExpectation.Absent("DoSomething") with + { + DisplayTextSuffix = "<>" + }, + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38074")] @@ -11250,9 +11879,11 @@ void M(RankedMusicians m) } }"; // VerifyItemExistsAsync also tests with the item typed. - await VerifyItemExistsAsync(markup, "BillyJoel"); - await VerifyItemExistsAsync(markup, "EveryoneElse"); - await VerifyItemIsAbsentAsync(markup, "Equals"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("BillyJoel"), + ItemExpectation.Exists("EveryoneElse"), + ItemExpectation.Absent("Equals"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49072")] @@ -11278,9 +11909,11 @@ void M(RankedMusicians m) } }"; // VerifyItemExistsAsync also tests with the item typed. - await VerifyItemExistsAsync(markup, "BillyJoel"); - await VerifyItemExistsAsync(markup, "EveryoneElse"); - await VerifyItemIsAbsentAsync(markup, "Equals"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("BillyJoel"), + ItemExpectation.Exists("EveryoneElse"), + ItemExpectation.Absent("Equals"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49072")] @@ -11306,9 +11939,11 @@ void M(C m) } }"; // VerifyItemExistsAsync also tests with the item typed. - await VerifyItemExistsAsync(markup, "BillyJoel"); - await VerifyItemExistsAsync(markup, "EveryoneElse"); - await VerifyItemIsAbsentAsync(markup, "Equals"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("BillyJoel"), + ItemExpectation.Exists("EveryoneElse"), + ItemExpectation.Absent("Equals"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49072")] @@ -11353,9 +11988,11 @@ void M(RankedMusicians m) } }"; // VerifyItemExistsAsync also tests with the item typed. - await VerifyItemExistsAsync(markup, "BillyJoel"); - await VerifyItemExistsAsync(markup, "EveryoneElse"); - await VerifyItemIsAbsentAsync(markup, "Equals"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("BillyJoel"), + ItemExpectation.Exists("EveryoneElse"), + ItemExpectation.Absent("Equals"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49072")] @@ -11381,9 +12018,11 @@ void M(RankedMusicians m) } }"; // VerifyItemExistsAsync also tests with the item typed. - await VerifyItemExistsAsync(markup, "BillyJoel"); - await VerifyItemExistsAsync(markup, "EveryoneElse"); - await VerifyItemIsAbsentAsync(markup, "Equals"); + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("BillyJoel"), + ItemExpectation.Exists("EveryoneElse"), + ItemExpectation.Absent("Equals"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/49609")] @@ -11550,12 +12189,15 @@ void M(T x) where T : I1, I2 } } "; - await VerifyItemIsAbsentAsync(source, "M0"); - await VerifyItemExistsAsync(source, "M1"); - await VerifyItemExistsAsync(source, "M2"); - await VerifyItemExistsAsync(source, "M3"); - await VerifyItemExistsAsync(source, "P1"); - await VerifyItemExistsAsync(source, "E1"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("M0"), + + ItemExpectation.Exists("M1"), + ItemExpectation.Exists("M2"), + ItemExpectation.Exists("M3"), + ItemExpectation.Exists("P1"), + ItemExpectation.Exists("E1") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58081")] @@ -11577,10 +12219,12 @@ void TestMethod(TestStruct* a) } } "; - await VerifyItemExistsAsync(source, "X"); - await VerifyItemExistsAsync(source, "Y"); - await VerifyItemExistsAsync(source, "Method"); - await VerifyItemExistsAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("X"), + ItemExpectation.Exists("Y"), + ItemExpectation.Exists("Method"), + ItemExpectation.Exists("ToString") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58081")] @@ -11602,10 +12246,12 @@ await a->$$ } } "; - await VerifyItemExistsAsync(source, "X"); - await VerifyItemExistsAsync(source, "Y"); - await VerifyItemExistsAsync(source, "Method"); - await VerifyItemExistsAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("X"), + ItemExpectation.Exists("Y"), + ItemExpectation.Exists("Method"), + ItemExpectation.Exists("ToString") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58081")] @@ -11629,10 +12275,12 @@ TestLambda TestMethod() } } "; - await VerifyItemExistsAsync(source, "X"); - await VerifyItemExistsAsync(source, "Y"); - await VerifyItemExistsAsync(source, "Method"); - await VerifyItemExistsAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("X"), + ItemExpectation.Exists("Y"), + ItemExpectation.Exists("Method"), + ItemExpectation.Exists("ToString") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58081")] @@ -11667,8 +12315,10 @@ void TestMethod() => Overloaded(a => a->$$); } "; - await VerifyItemExistsAsync(source, "X"); - await VerifyItemExistsAsync(source, "Y"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("X"), + ItemExpectation.Exists("Y") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58081")] @@ -11703,8 +12353,10 @@ void TestMethod() => Overloaded((TestStruct1* a) => a->$$); } "; - await VerifyItemExistsAsync(source, "X"); - await VerifyItemIsAbsentAsync(source, "Y"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("X"), + ItemExpectation.Absent("Y") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58081")] @@ -11761,8 +12413,10 @@ void TestMethod() => Overloaded(a => a.$$); } "; - await VerifyItemIsAbsentAsync(source, "X"); - await VerifyItemIsAbsentAsync(source, "Y"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("X"), + ItemExpectation.Absent("Y") + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58081")] @@ -11797,8 +12451,10 @@ void TestMethod() => Overloaded((TestStruct1* a) => a.$$); } "; - await VerifyItemIsAbsentAsync(source, "X"); - await VerifyItemIsAbsentAsync(source, "Y"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("X"), + ItemExpectation.Absent("Y") + ]); } [InlineData("m.MyObject?.$$MyValue!!()")] @@ -12113,10 +12769,12 @@ public async Task EnumBaseList1(string underlyingType) { var source = "enum E : $$"; - await VerifyItemExistsAsync(source, "System"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("System"), - // Not accessible in the given context - await VerifyItemIsAbsentAsync(source, underlyingType); + // Not accessible in the given context + ItemExpectation.Absent(underlyingType), + ]); } [Theory, MemberData(nameof(ValidEnumUnderlyingTypeNames))] @@ -12146,13 +12804,16 @@ public async Task EnumBaseList3(string underlyingType) enum E : $$ """; - await VerifyItemExistsAsync(source, "System"); - await VerifyItemExistsAsync(source, underlyingType); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("System"), + + ItemExpectation.Exists(underlyingType), - // Verify that other things from `System` namespace are not present - await VerifyItemIsAbsentAsync(source, "Console"); - await VerifyItemIsAbsentAsync(source, "Action"); - await VerifyItemIsAbsentAsync(source, "DateTime"); + // Verify that other things from `System` namespace are not present + ItemExpectation.Absent("Console"), + ItemExpectation.Absent("Action"), + ItemExpectation.Absent("DateTime") + ]); } [Theory, MemberData(nameof(ValidEnumUnderlyingTypeNames))] @@ -12166,13 +12827,15 @@ namespace MyNamespace enum E : global::$$ """; - await VerifyItemIsAbsentAsync(source, "E"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("E"), - await VerifyItemExistsAsync(source, "System"); - await VerifyItemIsAbsentAsync(source, "MyNamespace"); + ItemExpectation.Exists("System"), + ItemExpectation.Absent("MyNamespace"), - // Not accessible in the given context - await VerifyItemIsAbsentAsync(source, underlyingType); + // Not accessible in the given context + ItemExpectation.Absent(underlyingType) + ]); } [Theory, MemberData(nameof(ValidEnumUnderlyingTypeNames))] @@ -12180,14 +12843,16 @@ public async Task EnumBaseList5(string underlyingType) { var source = "enum E : System.$$"; - await VerifyItemIsAbsentAsync(source, "System"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("System"), - await VerifyItemExistsAsync(source, underlyingType); + ItemExpectation.Exists(underlyingType), - // Verify that other things from `System` namespace are not present - await VerifyItemIsAbsentAsync(source, "Console"); - await VerifyItemIsAbsentAsync(source, "Action"); - await VerifyItemIsAbsentAsync(source, "DateTime"); + // Verify that other things from `System` namespace are not present + ItemExpectation.Absent("Console"), + ItemExpectation.Absent("Action"), + ItemExpectation.Absent("DateTime") + ]); } [Theory, MemberData(nameof(ValidEnumUnderlyingTypeNames))] @@ -12195,14 +12860,16 @@ public async Task EnumBaseList6(string underlyingType) { var source = "enum E : global::System.$$"; - await VerifyItemIsAbsentAsync(source, "System"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("System"), - await VerifyItemExistsAsync(source, underlyingType); + ItemExpectation.Exists(underlyingType), - // Verify that other things from `System` namespace are not present - await VerifyItemIsAbsentAsync(source, "Console"); - await VerifyItemIsAbsentAsync(source, "Action"); - await VerifyItemIsAbsentAsync(source, "DateTime"); + // Verify that other things from `System` namespace are not present + ItemExpectation.Absent("Console"), + ItemExpectation.Absent("Action"), + ItemExpectation.Absent("DateTime") + ]); } [Fact] @@ -12269,15 +12936,17 @@ public async Task EnumBaseList11(string underlyingType) enum E : MySystem.$$ """; - await VerifyItemIsAbsentAsync(source, "System"); - await VerifyItemIsAbsentAsync(source, "MySystem"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Absent("System"), + ItemExpectation.Absent("MySystem"), - await VerifyItemExistsAsync(source, underlyingType); + ItemExpectation.Exists(underlyingType), - // Verify that other things from `System` namespace are not present - await VerifyItemIsAbsentAsync(source, "Console"); - await VerifyItemIsAbsentAsync(source, "Action"); - await VerifyItemIsAbsentAsync(source, "DateTime"); + // Verify that other things from `System` namespace are not present + ItemExpectation.Absent("Console"), + ItemExpectation.Absent("Action"), + ItemExpectation.Absent("DateTime") + ]); } [Fact] @@ -12358,9 +13027,11 @@ void M(string s) } """; - await VerifyItemExistsAsync(source, "endIndex"); - await VerifyItemExistsAsync(source, "Test"); - await VerifyItemExistsAsync(source, "C"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("endIndex"), + ItemExpectation.Exists("Test"), + ItemExpectation.Exists("C"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66903")] @@ -12379,9 +13050,11 @@ void M(string s) } """; - await VerifyItemExistsAsync(source, "endIndex"); - await VerifyItemExistsAsync(source, "Test"); - await VerifyItemExistsAsync(source, "C"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("endIndex"), + ItemExpectation.Exists("Test"), + ItemExpectation.Exists("C"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25572")] @@ -12404,10 +13077,12 @@ void M() } """; - await VerifyItemExistsAsync(source, "foo"); - await VerifyItemExistsAsync(source, "M"); - await VerifyItemExistsAsync(source, "System"); - await VerifyItemIsAbsentAsync(source, "Int32"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("foo"), + ItemExpectation.Exists("M"), + ItemExpectation.Exists("System"), + ItemExpectation.Absent("Int32"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25572")] @@ -12428,9 +13103,11 @@ void A() { } } """; - await VerifyItemExistsAsync(source, "System"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemIsAbsentAsync(source, "other"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("System"), + ItemExpectation.Exists("C"), + ItemExpectation.Absent("other"), + ]); } public static readonly IEnumerable PatternMatchingPrecedingPatterns = new object[][] @@ -12463,12 +13140,14 @@ public async Task PatternMatching_01(string precedingPattern) var expression = $"return input {precedingPattern} Constants.$$"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemIsAbsentAsync(source, "D"); - await VerifyItemIsAbsentAsync(source, "M"); - await VerifyItemExistsAsync(source, "R"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("C"), + ItemExpectation.Absent("D"), + ItemExpectation.Absent("M"), + ItemExpectation.Exists("R"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -12478,12 +13157,14 @@ public async Task PatternMatching_02(string precedingPattern) var expression = $"return input {precedingPattern} Constants.R.$$"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemIsAbsentAsync(source, "C"); - await VerifyItemIsAbsentAsync(source, "D"); - await VerifyItemIsAbsentAsync(source, "M"); - await VerifyItemIsAbsentAsync(source, "R"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Absent("C"), + ItemExpectation.Absent("D"), + ItemExpectation.Absent("M"), + ItemExpectation.Absent("R"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -12493,9 +13174,11 @@ public async Task PatternMatching_03(string precedingPattern) var expression = $"return input {precedingPattern} $$"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemExistsAsync(source, "Constants"); - await VerifyItemExistsAsync(source, "System"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("C"), + ItemExpectation.Exists("Constants"), + ItemExpectation.Exists("System"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -12506,8 +13189,12 @@ public async Task PatternMatching_04(string precedingPattern) var source = WrapPatternMatchingSource(expression); // In scripts, we also get a Script class containing our defined types - await VerifyItemExistsAsync(source, "C", sourceCodeKind: SourceCodeKind.Regular); - await VerifyItemExistsAsync(source, "Constants", sourceCodeKind: SourceCodeKind.Regular); + await VerifyExpectedItemsAsync(source, + [ + ItemExpectation.Exists("C"), + ItemExpectation.Exists("Constants"), + ], + sourceCodeKind: SourceCodeKind.Regular); await VerifyItemExistsAsync(source, "System"); } @@ -12517,10 +13204,12 @@ public async Task PatternMatching_05() var expression = $"return $$ is Constants.A"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "input"); - await VerifyItemExistsAsync(source, "Constants"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemExistsAsync(source, "M"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("input"), + ItemExpectation.Exists("Constants"), + ItemExpectation.Exists("C"), + ItemExpectation.Exists("M"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -12530,12 +13219,14 @@ public async Task PatternMatching_06(string precedingPattern) var expression = $"return input {precedingPattern} Constants.$$.A"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemIsAbsentAsync(source, "D"); - await VerifyItemIsAbsentAsync(source, "M"); - await VerifyItemExistsAsync(source, "R"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("C"), + ItemExpectation.Absent("D"), + ItemExpectation.Absent("M"), + ItemExpectation.Exists("R"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -12545,12 +13236,14 @@ public async Task PatternMatching_07(string precedingPattern) var expression = $"return input {precedingPattern} Enum.$$"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemExistsAsync(source, "D"); - await VerifyItemIsAbsentAsync(source, "M"); - await VerifyItemIsAbsentAsync(source, "R"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("C"), + ItemExpectation.Exists("D"), + ItemExpectation.Absent("M"), + ItemExpectation.Absent("R"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -12560,14 +13253,16 @@ public async Task PatternMatching_08(string precedingPattern) var expression = $"return input {precedingPattern} nameof(Constants.$$"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemExistsAsync(source, "D"); - await VerifyItemExistsAsync(source, "E"); - await VerifyItemExistsAsync(source, "M"); - await VerifyItemExistsAsync(source, "R"); - await VerifyItemExistsAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("C"), + ItemExpectation.Exists("D"), + ItemExpectation.Exists("E"), + ItemExpectation.Exists("M"), + ItemExpectation.Exists("R"), + ItemExpectation.Exists("ToString"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -12577,11 +13272,13 @@ public async Task PatternMatching_09(string precedingPattern) var expression = $"return input {precedingPattern} nameof(Constants.R.$$"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemExistsAsync(source, "D"); - await VerifyItemExistsAsync(source, "E"); - await VerifyItemExistsAsync(source, "M"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("D"), + ItemExpectation.Exists("E"), + ItemExpectation.Exists("M"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -12591,13 +13288,15 @@ public async Task PatternMatching_10(string precedingPattern) var expression = $"return input {precedingPattern} nameof(Constants.$$.A"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemExistsAsync(source, "D"); - await VerifyItemExistsAsync(source, "E"); - await VerifyItemExistsAsync(source, "M"); - await VerifyItemExistsAsync(source, "R"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("C"), + ItemExpectation.Exists("D"), + ItemExpectation.Exists("E"), + ItemExpectation.Exists("M"), + ItemExpectation.Exists("R"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -12607,12 +13306,14 @@ public async Task PatternMatching_11(string precedingPattern) var expression = $"return input {precedingPattern} [Constants.R(Constants.$$, nameof(Constants.R))]"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemIsAbsentAsync(source, "D"); - await VerifyItemIsAbsentAsync(source, "M"); - await VerifyItemExistsAsync(source, "R"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("C"), + ItemExpectation.Absent("D"), + ItemExpectation.Absent("M"), + ItemExpectation.Exists("R"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -12622,12 +13323,14 @@ public async Task PatternMatching_12(string precedingPattern) var expression = $"return input {precedingPattern} [Constants.R(Constants.$$), nameof(Constants.R)]"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "A"); - await VerifyItemExistsAsync(source, "B"); - await VerifyItemExistsAsync(source, "C"); - await VerifyItemIsAbsentAsync(source, "D"); - await VerifyItemIsAbsentAsync(source, "M"); - await VerifyItemExistsAsync(source, "R"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("A"), + ItemExpectation.Exists("B"), + ItemExpectation.Exists("C"), + ItemExpectation.Absent("D"), + ItemExpectation.Absent("M"), + ItemExpectation.Exists("R"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -12637,8 +13340,10 @@ public async Task PatternMatching_13(string precedingPattern) var expression = $"return input {precedingPattern} [Constants.R(Constants.A) {{ P.$$: Constants.A, InstanceProperty: 153 }}, nameof(Constants.R)]"; var source = WrapPatternMatchingSource(expression); - await VerifyItemExistsAsync(source, "Length"); - await VerifyItemIsAbsentAsync(source, "Constants"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("Length"), + ItemExpectation.Absent("Constants"), + ]); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/70226")] @@ -12648,9 +13353,11 @@ public async Task PatternMatching_14(string precedingPattern) var expression = $"return input {precedingPattern} [Constants.R(Constants.A) {{ P: $$ }}, nameof(Constants.R)]"; var source = WrapPatternMatchingSource(expression); - await VerifyItemIsAbsentAsync(source, "InstanceProperty"); - await VerifyItemIsAbsentAsync(source, "P"); - await VerifyItemExistsAsync(source, "Constants"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("Constants"), + ItemExpectation.Absent("InstanceProperty"), + ItemExpectation.Absent("P"), + ]); } private static string WrapPatternMatchingSource(string returnedExpression) @@ -12894,8 +13601,10 @@ public enum Status Closed, } """; - await VerifyItemExistsAsync(source, "Undisclosed"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("Undisclosed"), + ItemExpectation.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -12924,8 +13633,10 @@ public enum Status Closed, } """; - await VerifyItemExistsAsync(source, "Undisclosed"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("Undisclosed"), + ItemExpectation.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -12956,8 +13667,10 @@ public enum StatusEn Closed, } """; - await VerifyItemExistsAsync(source, "StatusEn"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("StatusEn"), + ItemExpectation.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -12988,8 +13701,10 @@ public enum StatusEn Closed, } """; - await VerifyItemExistsAsync(source, "Undisclosed"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("Undisclosed"), + ItemExpectation.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -13021,8 +13736,10 @@ public enum StatusEn Closed, } """; - await VerifyItemExistsAsync(source, "Undisclosed"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("Undisclosed"), + ItemExpectation.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -13049,8 +13766,10 @@ public enum Status Closed, } """; - await VerifyItemExistsAsync(source, "Undisclosed"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("Undisclosed"), + ItemExpectation.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -13077,8 +13796,10 @@ public enum Status Closed, } """; - await VerifyItemExistsAsync(source, "Undisclosed"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("Undisclosed"), + ItemExpectation.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -13107,8 +13828,10 @@ public enum StatusEn Closed, } """; - await VerifyItemExistsAsync(source, "StatusEn"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("StatusEn"), + ItemExpectation.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -13137,8 +13860,10 @@ public enum StatusEn Closed, } """; - await VerifyItemExistsAsync(source, "Undisclosed"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("Undisclosed"), + ItemExpectation.Absent("ToString"), + ]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75350")] @@ -13168,8 +13893,10 @@ public enum StatusEn Closed, } """; - await VerifyItemExistsAsync(source, "Undisclosed"); - await VerifyItemIsAbsentAsync(source, "ToString"); + await VerifyExpectedItemsAsync(source, [ + ItemExpectation.Exists("Undisclosed"), + ItemExpectation.Absent("ToString"), + ]); } #region Collection expressions diff --git a/src/EditorFeatures/CSharpTest/DocumentationComments/DocumentationCommentTests.cs b/src/EditorFeatures/CSharpTest/DocumentationComments/DocumentationCommentTests.cs index ec08b003714f0..00c36eca01d29 100644 --- a/src/EditorFeatures/CSharpTest/DocumentationComments/DocumentationCommentTests.cs +++ b/src/EditorFeatures/CSharpTest/DocumentationComments/DocumentationCommentTests.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using Microsoft.CodeAnalysis.DocumentationComments; using Microsoft.CodeAnalysis.Editor.CSharp.DocumentationComments; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; @@ -17,24 +15,28 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.DocumentationComments; [Trait(Traits.Feature, Traits.Features.DocumentationComments)] -public class DocumentationCommentTests : AbstractDocumentationCommentTests +public sealed class DocumentationCommentTests : AbstractDocumentationCommentTests { [WpfFact] public void TypingCharacter_Class() { var code = -@"//$$ -class C -{ -}"; + """ + //$$ + class C + { + } + """; var expected = -@"/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// $$ + /// + class C + { + } + """; VerifyTypingCharacter(code, expected); } @@ -43,14 +45,18 @@ class C public void TypingCharacter_Record() { var code = -@"//$$ -record R;"; + """ + //$$ + record R; + """; var expected = -@"/// -/// $$ -/// -record R;"; + """ + /// + /// $$ + /// + record R; + """; VerifyTypingCharacter(code, expected); } @@ -59,14 +65,18 @@ record R;"; public void TypingCharacter_RecordStruct() { var code = -@"//$$ -record struct R;"; + """ + //$$ + record struct R; + """; var expected = -@"/// -/// $$ -/// -record struct R;"; + """ + /// + /// $$ + /// + record struct R; + """; VerifyTypingCharacter(code, expected); } @@ -75,16 +85,20 @@ record struct R;"; public void TypingCharacter_RecordWithPositionalParameters() { var code = -@"//$$ -record R(string S, int I);"; + """ + //$$ + record R(string S, int I); + """; var expected = -@"/// -/// $$ -/// -/// -/// -record R(string S, int I);"; + """ + /// + /// $$ + /// + /// + /// + record R(string S, int I); + """; VerifyTypingCharacter(code, expected); } @@ -93,16 +107,20 @@ record R(string S, int I);"; public void TypingCharacter_ClassParameters() { var code = -@"//$$ -class R(string S, int I);"; + """ + //$$ + class R(string S, int I); + """; var expected = -@"/// -/// $$ -/// -/// -/// -class R(string S, int I);"; + """ + /// + /// $$ + /// + /// + /// + class R(string S, int I); + """; VerifyTypingCharacter(code, expected); } @@ -111,16 +129,20 @@ class R(string S, int I);"; public void TypingCharacter_RecordStructWithPositionalParameters() { var code = -@"//$$ -record struct R(string S, int I);"; + """ + //$$ + record struct R(string S, int I); + """; var expected = -@"/// -/// $$ -/// -/// -/// -record struct R(string S, int I);"; + """ + /// + /// $$ + /// + /// + /// + record struct R(string S, int I); + """; VerifyTypingCharacter(code, expected); } @@ -129,16 +151,20 @@ record struct R(string S, int I);"; public void TypingCharacter_StructParameters() { var code = -@"//$$ -struct R(string S, int I);"; + """ + //$$ + struct R(string S, int I); + """; var expected = -@"/// -/// $$ -/// -/// -/// -struct R(string S, int I);"; + """ + /// + /// $$ + /// + /// + /// + struct R(string S, int I); + """; VerifyTypingCharacter(code, expected); } @@ -146,33 +172,67 @@ struct R(string S, int I);"; [WpfFact] public void TypingCharacter_Class_NewLine() { - var code = "//$$\r\nclass C\r\n{\r\n}"; + var code = """ + //$$ + class C + { + } + """; + + var expected = """ + /// + /// $$ + /// + class C + { + } + """; + + VerifyTypingCharacter(code, expected, newLine: """ + - var expected = "/// \n/// $$\n/// \r\nclass C\r\n{\r\n}"; + """); - VerifyTypingCharacter(code, expected, newLine: "\n"); + code = """ + //$$ + class C + { + } + """; - code = "//$$\r\nclass C\r\n{\r\n}"; + expected = """ + /// + /// $$ + /// + class C + { + } + """; - expected = "/// \r\n/// $$\r\n/// \r\nclass C\r\n{\r\n}"; + VerifyTypingCharacter(code, expected, newLine: """ - VerifyTypingCharacter(code, expected, newLine: "\r\n"); + + """); } [WpfFact] public void TypingCharacter_Class_AutoGenerateXmlDocCommentsOff() { var code = -@"//$$ -class C -{ -}"; + """ + //$$ + class C + { + } + """; var expected = -@"///$$ -class C -{ -}"; + """ + ///$$ + class C + { + } + """; VerifyTypingCharacter(code, expected, globalOptions: new OptionsCollection(LanguageNames.CSharp) { @@ -184,23 +244,27 @@ class C public void TypingCharacter_Method() { var code = -@"class C -{ - //$$ - int M(int goo) { return 0; } -}"; - - var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - /// - int M(int goo) { return 0; } -}"; + """ + class C + { + //$$ + int M(int goo) { return 0; } + } + """; + + var expected = + """ + class C + { + /// + /// $$ + /// + /// + /// + /// + int M(int goo) { return 0; } + } + """; VerifyTypingCharacter(code, expected); } @@ -209,32 +273,36 @@ public void TypingCharacter_Method() public void TypingCharacter_Method_WithExceptions() { var code = -@"class C -{ - //$$ - int M(int goo) - { - if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); - return 0; - } -}"; - - var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - /// - /// - int M(int goo) - { - if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); - return 0; - } -}"; + """ + class C + { + //$$ + int M(int goo) + { + if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); + return 0; + } + } + """; + + var expected = + """ + class C + { + /// + /// $$ + /// + /// + /// + /// + /// + int M(int goo) + { + if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); + return 0; + } + } + """; VerifyTypingCharacter(code, expected); } @@ -243,33 +311,37 @@ int M(int goo) public void TypingCharacter_Constructor_WithExceptions() { var code = -@"class C -{ - //$$ - public C(int goo) - { - if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); - throw null; - throw null; - } -}"; - - var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - /// - public C(int goo) - { - if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); - throw null; - throw null; - } -}"; + """ + class C + { + //$$ + public C(int goo) + { + if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); + throw null; + throw null; + } + } + """; + + var expected = + """ + class C + { + /// + /// $$ + /// + /// + /// + /// + public C(int goo) + { + if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); + throw null; + throw null; + } + } + """; VerifyTypingCharacter(code, expected); } @@ -278,59 +350,63 @@ public C(int goo) public void TypingCharacter_Constructor_WithExceptions_Caught() { // This result is wrong, but we can't do better as long as we only check syntax. - var code = @" -using System; - -class C -{ - //$$ - public C(int goo) - { - try - { - if (goo == 10) - throw new Exception(); - if (goo == 9) - throw new ArgumentOutOfRangeException(); - } - catch (ArgumentException) - { - } - - throw null; - throw null; - } -}"; - - var expected = @" -using System; - -class C -{ - /// - /// $$ - /// - /// - /// - /// - /// - public C(int goo) - { - try - { - if (goo == 10) - throw new Exception(); - if (goo == 9) - throw new ArgumentOutOfRangeException(); - } - catch (ArgumentException) - { - } - - throw null; - throw null; - } -}"; + var code = """ + + using System; + + class C + { + //$$ + public C(int goo) + { + try + { + if (goo == 10) + throw new Exception(); + if (goo == 9) + throw new ArgumentOutOfRangeException(); + } + catch (ArgumentException) + { + } + + throw null; + throw null; + } + } + """; + + var expected = """ + + using System; + + class C + { + /// + /// $$ + /// + /// + /// + /// + /// + public C(int goo) + { + try + { + if (goo == 10) + throw new Exception(); + if (goo == 9) + throw new ArgumentOutOfRangeException(); + } + catch (ArgumentException) + { + } + + throw null; + throw null; + } + } + """; VerifyTypingCharacter(code, expected); } @@ -339,23 +415,27 @@ public C(int goo) public void TypingCharacter_Method_WithVerbatimParams() { var code = -@"class C -{ - //$$ - int M<@int>(int @goo) { return 0; } -}"; - - var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - /// - int M<@int>(int @goo) { return 0; } -}"; + """ + class C + { + //$$ + int M<@int>(int @goo) { return 0; } + } + """; + + var expected = + """ + class C + { + /// + /// $$ + /// + /// + /// + /// + int M<@int>(int @goo) { return 0; } + } + """; VerifyTypingCharacter(code, expected); } @@ -364,20 +444,24 @@ public void TypingCharacter_Method_WithVerbatimParams() public void TypingCharacter_AutoProperty() { var code = -@"class C -{ - //$$ - int P { get; set; } -}"; + """ + class C + { + //$$ + int P { get; set; } + } + """; var expected = -@"class C -{ - /// - /// $$ - /// - int P { get; set; } -}"; + """ + class C + { + /// + /// $$ + /// + int P { get; set; } + } + """; VerifyTypingCharacter(code, expected); } @@ -386,28 +470,32 @@ public void TypingCharacter_AutoProperty() public void TypingCharacter_Property() { var code = -@"class C -{ - //$$ - int P - { - get { return 0; } - set { } - } -}"; - - var expected = -@"class C -{ - /// - /// $$ - /// - int P - { - get { return 0; } - set { } - } -}"; + """ + class C + { + //$$ + int P + { + get { return 0; } + set { } + } + } + """; + + var expected = + """ + class C + { + /// + /// $$ + /// + int P + { + get { return 0; } + set { } + } + } + """; VerifyTypingCharacter(code, expected); } @@ -416,30 +504,34 @@ int P public void TypingCharacter_Indexer() { var code = -@"class C -{ - //$$ - int this[int index] - { - get { return 0; } - set { } - } -}"; - - var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - int this[int index] - { - get { return 0; } - set { } - } -}"; + """ + class C + { + //$$ + int this[int index] + { + get { return 0; } + set { } + } + } + """; + + var expected = + """ + class C + { + /// + /// $$ + /// + /// + /// + int this[int index] + { + get { return 0; } + set { } + } + } + """; VerifyTypingCharacter(code, expected); } @@ -448,22 +540,26 @@ int this[int index] public void TypingCharacter_VoidMethod1() { var code = -@"class C -{ - //$$ - void M(int goo) { } -}"; + """ + class C + { + //$$ + void M(int goo) { } + } + """; var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - void M(int goo) { } -}"; + """ + class C + { + /// + /// $$ + /// + /// + /// + void M(int goo) { } + } + """; VerifyTypingCharacter(code, expected); } @@ -472,22 +568,26 @@ void M(int goo) { } public void TypingCharacter_VoidMethod_WithVerbatimParams() { var code = -@"class C -{ - //$$ - void M<@T>(int @int) { } -}"; + """ + class C + { + //$$ + void M<@T>(int @int) { } + } + """; var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - void M<@T>(int @int) { } -}"; + """ + class C + { + /// + /// $$ + /// + /// + /// + void M<@T>(int @int) { } + } + """; VerifyTypingCharacter(code, expected); } @@ -496,38 +596,46 @@ void M<@T>(int @int) { } public void TypingCharacter_VoidMethod2() { var code = -@"class C -{ - //$$ - void Method() { } -}"; - var expected = -@"class C -{ - /// - /// $$ - /// - void Method() { } -}"; + """ + class C + { + //$$ + void Method() { } + } + """; + var expected = + """ + class C + { + /// + /// $$ + /// + void Method() { } + } + """; VerifyTypingCharacter(code, expected); } [WpfFact] public void TypingCharacter_NotWhenDocCommentExists1() { - var code = @" -/// -//$$ -class C -{ -}"; + var code = """ - var expected = @" -/// -///$$ -class C -{ -}"; + /// + //$$ + class C + { + } + """; + + var expected = """ + + /// + ///$$ + class C + { + } + """; VerifyTypingCharacter(code, expected); } @@ -535,21 +643,25 @@ class C [WpfFact] public void TypingCharacter_NotWhenDocCommentExists2() { - var code = @" -/// + var code = """ -//$$ -class C -{ -}"; + /// - var expected = @" -/// + //$$ + class C + { + } + """; -///$$ -class C -{ -}"; + var expected = """ + + /// + + ///$$ + class C + { + } + """; VerifyTypingCharacter(code, expected); } @@ -557,21 +669,25 @@ class C [WpfFact] public void TypingCharacter_NotWhenDocCommentExists3() { - var code = @" -class B { } /// + var code = """ -//$$ -class C -{ -}"; + class B { } /// - var expected = @" -class B { } /// + //$$ + class C + { + } + """; -///$$ -class C -{ -}"; + var expected = """ + + class B { } /// + + ///$$ + class C + { + } + """; VerifyTypingCharacter(code, expected); } @@ -580,18 +696,22 @@ class C public void TypingCharacter_NotWhenDocCommentExists4() { var code = -@"//$$ -/// -class C -{ -}"; + """ + //$$ + /// + class C + { + } + """; var expected = -@"///$$ -/// -class C -{ -}"; + """ + ///$$ + /// + class C + { + } + """; VerifyTypingCharacter(code, expected); } @@ -600,20 +720,24 @@ class C public void TypingCharacter_NotWhenDocCommentExists5() { var code = -@"class C -{ - //$$ - /// - int M(int goo) { return 0; } -}"; + """ + class C + { + //$$ + /// + int M(int goo) { return 0; } + } + """; var expected = -@"class C -{ - ///$$ - /// - int M(int goo) { return 0; } -}"; + """ + class C + { + ///$$ + /// + int M(int goo) { return 0; } + } + """; VerifyTypingCharacter(code, expected); } @@ -622,22 +746,26 @@ public void TypingCharacter_NotWhenDocCommentExists5() public void TypingCharacter_NotInsideMethodBody1() { var code = -@"class C -{ - void M(int goo) - { - //$$ - } -}"; + """ + class C + { + void M(int goo) + { + //$$ + } + } + """; var expected = -@"class C -{ - void M(int goo) - { - ///$$ - } -}"; + """ + class C + { + void M(int goo) + { + ///$$ + } + } + """; VerifyTypingCharacter(code, expected); } @@ -646,24 +774,28 @@ void M(int goo) public void TypingCharacter_NotInsideMethodBody2() { var code = -@"class C -{ - /// - void M(int goo) - { - //$$ - } -}"; - - var expected = -@"class C -{ - /// - void M(int goo) - { - ///$$ - } -}"; + """ + class C + { + /// + void M(int goo) + { + //$$ + } + } + """; + + var expected = + """ + class C + { + /// + void M(int goo) + { + ///$$ + } + } + """; VerifyTypingCharacter(code, expected); } @@ -672,14 +804,18 @@ void M(int goo) public void TypingCharacter_NotAfterClassName() { var code = -@"class C//$$ -{ -}"; + """ + class C//$$ + { + } + """; var expected = -@"class C///$$ -{ -}"; + """ + class C///$$ + { + } + """; VerifyTypingCharacter(code, expected); } @@ -688,14 +824,18 @@ public void TypingCharacter_NotAfterClassName() public void TypingCharacter_NotAfterOpenBrace() { var code = -@"class C -{//$$ -}"; + """ + class C + {//$$ + } + """; var expected = -@"class C -{///$$ -}"; + """ + class C + {///$$ + } + """; VerifyTypingCharacter(code, expected); } @@ -704,16 +844,20 @@ public void TypingCharacter_NotAfterOpenBrace() public void TypingCharacter_NotAfterCtorName() { var code = -@"class C -{ -C() //$$ -}"; + """ + class C + { + C() //$$ + } + """; var expected = -@"class C -{ -C() ///$$ -}"; + """ + class C + { + C() ///$$ + } + """; VerifyTypingCharacter(code, expected); } @@ -722,22 +866,26 @@ public void TypingCharacter_NotAfterCtorName() public void TypingCharacter_NotInsideCtor() { var code = -@"class C -{ -C() -{ -//$$ -} -}"; + """ + class C + { + C() + { + //$$ + } + } + """; var expected = -@"class C -{ -C() -{ -///$$ -} -}"; + """ + class C + { + C() + { + ///$$ + } + } + """; VerifyTypingCharacter(code, expected); } @@ -745,19 +893,23 @@ public void TypingCharacter_NotInsideCtor() [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/59081")] public void TypingCharacter_NotInTopLevel() { - var code = @" -using System; + var code = """ + + using System; -//$$ -Console.WriteLine(); -"; + //$$ + Console.WriteLine(); - var expected = @" -using System; + """; -///$$ -Console.WriteLine(); -"; + var expected = """ + + using System; + + ///$$ + Console.WriteLine(); + + """; VerifyTypingCharacter(code, expected); } @@ -765,19 +917,23 @@ public void TypingCharacter_NotInTopLevel() [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/59081")] public void TypingCharacter_NotInNamespace() { - var code = @" -using System; + var code = """ + + using System; + + //$$ + namespace NS { } -//$$ -namespace NS { } -"; + """; - var expected = @" -using System; + var expected = """ -///$$ -namespace NS { } -"; + using System; + + ///$$ + namespace NS { } + + """; VerifyTypingCharacter(code, expected); } @@ -786,18 +942,22 @@ namespace NS { } public void PressingEnter_InsertComment_Class1() { var code = -@"///$$ -class C -{ -}"; + """ + ///$$ + class C + { + } + """; var expected = -@"/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// $$ + /// + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -806,17 +966,21 @@ class C public void PressingEnter_InsertComment_Class1_AutoGenerateXmlDocCommentsOff() { var code = -@"///$$ -class C -{ -}"; + """ + ///$$ + class C + { + } + """; var expected = -@"/// -$$ -class C -{ -}"; + """ + /// + $$ + class C + { + } + """; VerifyPressingEnter(code, expected, globalOptions: new OptionsCollection(LanguageNames.CSharp) { @@ -828,17 +992,21 @@ class C public void PressingEnter_InsertComment_Class2() { var code = -@"///$$class C -{ -}"; + """ + ///$$class C + { + } + """; var expected = -@"/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// $$ + /// + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -847,17 +1015,21 @@ class C public void PressingEnter_InsertComment_Class3() { var code = -@"///$$[Goo] class C -{ -}"; + """ + ///$$[Goo] class C + { + } + """; var expected = -@"/// -/// $$ -/// -[Goo] class C -{ -}"; + """ + /// + /// $$ + /// + [Goo] class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -866,15 +1038,19 @@ [Goo] class C public void PressingEnter_InsertComment_NotAfterWhitespace() { var code = - @"/// $$class C -{ -}"; + """ + /// $$class C + { + } + """; var expected = -@"/// -/// $$class C -{ -}"; + """ + /// + /// $$class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -883,23 +1059,27 @@ public void PressingEnter_InsertComment_NotAfterWhitespace() public void PressingEnter_InsertComment_Method1() { var code = -@"class C -{ - ///$$ - int M(int goo) { return 0; } -}"; - - var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - /// - int M(int goo) { return 0; } -}"; + """ + class C + { + ///$$ + int M(int goo) { return 0; } + } + """; + + var expected = + """ + class C + { + /// + /// $$ + /// + /// + /// + /// + int M(int goo) { return 0; } + } + """; VerifyPressingEnter(code, expected); } @@ -908,22 +1088,26 @@ public void PressingEnter_InsertComment_Method1() public void PressingEnter_InsertComment_Method2() { var code = -@"class C -{ - ///$$int M(int goo) { return 0; } -}"; + """ + class C + { + ///$$int M(int goo) { return 0; } + } + """; var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - /// - int M(int goo) { return 0; } -}"; + """ + class C + { + /// + /// $$ + /// + /// + /// + /// + int M(int goo) { return 0; } + } + """; VerifyPressingEnter(code, expected); } @@ -932,23 +1116,27 @@ public void PressingEnter_InsertComment_Method2() public void PressingEnter_NotInMethodBody1() { var code = -@"class C -{ -void Goo() -{ -///$$ -} -}"; - - var expected = -@"class C -{ -void Goo() -{ -/// -$$ -} -}"; + """ + class C + { + void Goo() + { + ///$$ + } + } + """; + + var expected = + """ + class C + { + void Goo() + { + /// + $$ + } + } + """; VerifyPressingEnter(code, expected); } @@ -957,15 +1145,19 @@ void Goo() public void PressingEnter_NotInterleavedInClassName1() { var code = -@"class///$$ C -{ -}"; + """ + class///$$ C + { + } + """; var expected = -@"class/// -$$ C -{ -}"; + """ + class/// + $$ C + { + } + """; VerifyPressingEnter(code, expected); } @@ -974,15 +1166,19 @@ public void PressingEnter_NotInterleavedInClassName1() public void PressingEnter_NotInterleavedInClassName2() { var code = -@"class ///$$C -{ -}"; + """ + class ///$$C + { + } + """; var expected = -@"class /// -$$C -{ -}"; + """ + class /// + $$C + { + } + """; VerifyPressingEnter(code, expected); } @@ -991,15 +1187,19 @@ public void PressingEnter_NotInterleavedInClassName2() public void PressingEnter_NotInterleavedInClassName3() { var code = -@"class /// $$C -{ -}"; + """ + class /// $$C + { + } + """; var expected = -@"class /// -$$C -{ -}"; + """ + class /// + $$C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1009,15 +1209,19 @@ public void PressingEnter_NotInterleavedInClassName3() public void PressingEnter_NotAfterClassName1() { var code = -@"class C ///$$ -{ -}"; + """ + class C ///$$ + { + } + """; var expected = -@"class C /// -$$ -{ -}"; + """ + class C /// + $$ + { + } + """; VerifyPressingEnter(code, expected); } @@ -1026,15 +1230,19 @@ public void PressingEnter_NotAfterClassName1() public void PressingEnter_NotAfterClassName2() { var code = -@"class C /** $$ -{ -}"; + """ + class C /** $$ + { + } + """; var expected = -@"class C /** -$$ -{ -}"; + """ + class C /** + $$ + { + } + """; VerifyPressingEnter(code, expected); } @@ -1043,17 +1251,21 @@ public void PressingEnter_NotAfterClassName2() public void PressingEnter_NotAfterCtorName() { var code = -@"class C -{ -C() ///$$ -}"; + """ + class C + { + C() ///$$ + } + """; var expected = -@"class C -{ -C() /// -$$ -}"; + """ + class C + { + C() /// + $$ + } + """; VerifyPressingEnter(code, expected); } @@ -1062,23 +1274,27 @@ public void PressingEnter_NotAfterCtorName() public void PressingEnter_NotInsideCtor() { var code = -@"class C -{ -C() -{ -///$$ -} -}"; - - var expected = -@"class C -{ -C() -{ -/// -$$ -} -}"; + """ + class C + { + C() + { + ///$$ + } + } + """; + + var expected = + """ + class C + { + C() + { + /// + $$ + } + } + """; VerifyPressingEnter(code, expected); } @@ -1087,31 +1303,35 @@ public void PressingEnter_NotInsideCtor() public void PressingEnter_NotBeforeDocComment() { var code = -@" class c1 - { -$$/// - /// - /// - /// - public async Task goo() - { - var x = 1; - } - }"; - - var expected = -@" class c1 - { - -$$/// - /// - /// - /// - public async Task goo() - { - var x = 1; - } - }"; + """ + class c1 + { + $$/// + /// + /// + /// + public async Task goo() + { + var x = 1; + } + } + """; + + var expected = + """ + class c1 + { + + $$/// + /// + /// + /// + public async Task goo() + { + var x = 1; + } + } + """; VerifyPressingEnter(code, expected); } @@ -1120,19 +1340,23 @@ public async Task goo() public void PressingEnter_InsertSlashes1() { var code = -@"///$$ -/// -class C -{ -}"; + """ + ///$$ + /// + class C + { + } + """; var expected = -@"/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// $$ + /// + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1141,21 +1365,25 @@ class C public void PressingEnter_InsertSlashes2() { var code = -@"/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// $$ + /// + class C + { + } + """; var expected = -@"/// -/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// + /// $$ + /// + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1164,21 +1392,25 @@ class C public void PressingEnter_InsertSlashes3() { var code = -@" /// - /// $$ - /// - class C - { - }"; + """ + /// + /// $$ + /// + class C + { + } + """; var expected = -@" /// - /// - /// $$ - /// - class C - { - }"; + """ + /// + /// + /// $$ + /// + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1187,17 +1419,21 @@ class C public void PressingEnter_InsertSlashes4() { var code = -@"/// $$ -class C -{ -}"; + """ + /// $$ + class C + { + } + """; var expected = -@"/// -/// $$ -class C -{ -}"; + """ + /// + /// $$ + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1206,21 +1442,25 @@ class C public void PressingEnter_InsertSlashes5() { var code = -@" /// - /// $$ - /// - class C - { - }"; + """ + /// + /// $$ + /// + class C + { + } + """; var expected = -@" /// - /// - /// $$ - /// - class C - { - }"; + """ + /// + /// + /// $$ + /// + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1229,17 +1469,21 @@ class C public void PressingEnter_InsertSlashes6() { var code = -@"/// $$ -class C -{ -}"; + """ + /// $$ + class C + { + } + """; var expected = -@"/// -/// $$ -class C -{ -}"; + """ + /// + /// $$ + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1248,17 +1492,21 @@ class C public void PressingEnter_InsertSlashes7() { var code = -@" /// $$ - class C - { - }"; + """ + /// $$ + class C + { + } + """; var expected = -@" /// - /// $$ - class C - { - }"; + """ + /// + /// $$ + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1267,16 +1515,20 @@ class C public void PressingEnter_InsertSlashes8() { var code = -@"/// -/// -/// -///$$class C {}"; - var expected = -@"/// -/// -/// -/// -/// $$class C {}"; + """ + /// + /// + /// + ///$$class C {} + """; + var expected = + """ + /// + /// + /// + /// + /// $$class C {} + """; VerifyPressingEnter(code, expected); } @@ -1284,21 +1536,25 @@ public void PressingEnter_InsertSlashes8() public void PressingEnter_InsertSlashes9() { var code = -@"class C -{ - ///$$ - /// - int M(int goo) { return 0; } -}"; + """ + class C + { + ///$$ + /// + int M(int goo) { return 0; } + } + """; var expected = -@"class C -{ - /// - /// $$ - /// - int M(int goo) { return 0; } -}"; + """ + class C + { + /// + /// $$ + /// + int M(int goo) { return 0; } + } + """; VerifyPressingEnter(code, expected); } @@ -1307,16 +1563,20 @@ public void PressingEnter_InsertSlashes9() public void PressingEnter_InsertSlashes10() { var code = -@"/// -/// -/// -///$$Go ahead and add some slashes"; + """ + /// + /// + /// + ///$$Go ahead and add some slashes + """; var expected = -@"/// -/// -/// -/// -/// $$Go ahead and add some slashes"; + """ + /// + /// + /// + /// + /// $$Go ahead and add some slashes + """; VerifyPressingEnter(code, expected); } @@ -1325,29 +1585,33 @@ public void PressingEnter_InsertSlashes10() public void PressingEnter_InsertSlashes11() { var code = -@"class C -{ - /// - /// - /// - /// $$ - void Goo(int i) - { - } -}"; - - var expected = -@"class C -{ - /// - /// - /// - /// - /// $$ - void Goo(int i) - { - } -}"; + """ + class C + { + /// + /// + /// + /// $$ + void Goo(int i) + { + } + } + """; + + var expected = + """ + class C + { + /// + /// + /// + /// + /// $$ + void Goo(int i) + { + } + } + """; VerifyPressingEnter(code, expected); } @@ -1356,19 +1620,23 @@ void Goo(int i) public void PressingEnter_InsertSlashes12_AutoGenerateXmlDocCommentsOff() { var code = -@"///$$ -/// -class C -{ -}"; + """ + ///$$ + /// + class C + { + } + """; var expected = -@"/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// $$ + /// + class C + { + } + """; VerifyPressingEnter(code, expected, globalOptions: new OptionsCollection(LanguageNames.CSharp) { @@ -1380,19 +1648,23 @@ class C public void PressingEnter_DoNotInsertSlashes1() { var code = -@"/// -/// $$ -class C -{ -}"; + """ + /// + /// $$ + class C + { + } + """; var expected = -@"/// -/// -$$ -class C -{ -}"; + """ + /// + /// + $$ + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1401,16 +1673,20 @@ class C public void PressingEnter_DoNotInsertSlashes2() { var code = -@"/// + """ + /// -///$$ -class C{}"; + ///$$ + class C{} + """; var expected = -@"/// + """ + /// -/// -$$ -class C{}"; + /// + $$ + class C{} + """; VerifyPressingEnter(code, expected); } @@ -1418,23 +1694,27 @@ class C{}"; public void PressingEnter_ExtraSlashesAfterExteriorTrivia() { var code = -@"class C -{ -C() -{ -//////$$ -} -}"; - - var expected = -@"class C -{ -C() -{ -////// -///$$ -} -}"; + """ + class C + { + C() + { + //////$$ + } + } + """; + + var expected = + """ + class C + { + C() + { + ////// + ///$$ + } + } + """; VerifyPressingEnter(code, expected); } @@ -1443,20 +1723,24 @@ public void PressingEnter_ExtraSlashesAfterExteriorTrivia() public void PressingEnter_PreserveParams() { var code = -@"/// -/// -/// -/// $$ -static void Main(string[] args) -{ }"; - var expected = -@"/// -/// -/// -/// -/// $$ -static void Main(string[] args) -{ }"; + """ + /// + /// + /// + /// $$ + static void Main(string[] args) + { } + """; + var expected = + """ + /// + /// + /// + /// + /// $$ + static void Main(string[] args) + { } + """; VerifyPressingEnter(code, expected); } @@ -1465,27 +1749,31 @@ static void Main(string[] args) public void PressingEnter_InTextBeforeSpace() { const string code = -@"class C -{ - /// - /// hello$$ world - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello$$ world + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// hello - /// $$world - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello + /// $$world + /// + void M() + { + } + } + """; VerifyPressingEnter(code, expected); } @@ -1494,27 +1782,31 @@ void M() public void PressingEnter_Indentation1() { const string code = -@"class C -{ - /// - /// hello world$$ - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello world$$ + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// hello world - /// $$ - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello world + /// $$ + /// + void M() + { + } + } + """; VerifyPressingEnter(code, expected); } @@ -1523,27 +1815,31 @@ void M() public void PressingEnter_Indentation2() { const string code = -@"class C -{ - /// - /// hello $$world - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello $$world + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// hello - /// $$world - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello + /// $$world + /// + void M() + { + } + } + """; VerifyPressingEnter(code, expected); } @@ -1552,27 +1848,31 @@ void M() public void PressingEnter_Indentation3() { const string code = -@"class C -{ - /// - /// hello$$ world - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello$$ world + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// hello - /// $$world - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello + /// $$world + /// + void M() + { + } + } + """; VerifyPressingEnter(code, expected); } @@ -1581,27 +1881,31 @@ void M() public void PressingEnter_Indentation4() { const string code = -@"class C -{ - /// - /// $$hello world - /// - void M() - { - } -}"; + """ + class C + { + /// + /// $$hello world + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// - /// $$hello world - /// - void M() - { - } -}"; + """ + class C + { + /// + /// + /// $$hello world + /// + void M() + { + } + } + """; VerifyPressingEnter(code, expected); } @@ -1610,27 +1914,31 @@ void M() public void PressingEnter_Indentation5_UseTabs() { const string code = -@"class C -{ - /// - /// hello world$$ - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello world$$ + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// hello world - /// $$ - /// - void M() - { - } -}"; + """ + class C + { + /// + /// hello world + /// $$ + /// + void M() + { + } + } + """; VerifyPressingEnter(code, expected, useTabs: true); } @@ -1639,20 +1947,24 @@ void M() public void PressingEnter_Selection1() { var code = -@"/// -/// Hello [|World|]$$! -/// -class C -{ -}"; - var expected = -@"/// -/// Hello -/// $$! -/// -class C -{ -}"; + """ + /// + /// Hello [|World|]$$! + /// + class C + { + } + """; + var expected = + """ + /// + /// Hello + /// $$! + /// + class C + { + } + """; VerifyPressingEnter(code, expected); } @@ -1661,49 +1973,59 @@ class C public void PressingEnter_Selection2() { var code = -@"/// -/// Hello $$[|World|]! -/// -class C -{ -}"; - var expected = -@"/// -/// Hello -/// $$! -/// -class C -{ -}"; + """ + /// + /// Hello $$[|World|]! + /// + class C + { + } + """; + var expected = + """ + /// + /// Hello + /// $$! + /// + class C + { + } + """; VerifyPressingEnter(code, expected); } - [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/27223")] - public void PressingEnter_XmldocInStringLiteral() - { - var code = -@"class C -{ -C() -{ -string s = @"" -/// $$ -void M() {}"" -} -}"; - - var expected = -@"class C -{ -C() -{ -string s = @"" -/// -/// $$ -void M() {}"" -} -}"; + [WpfFact] + [WorkItem("https://github.com/dotnet/roslyn/issues/27223")] + [WorkItem("https://github.com/dotnet/roslyn/issues/49564")] + public void PressingEnter_XmlDocCommentInStringLiteral() + { + var code = + """ + class C + { + C() + { + string s = @" + /// $$ + void M() {}" + } + } + """; + + var expected = + """ + class C + { + C() + { + string s = @" + /// + $$ + void M() {}" + } + } + """; VerifyPressingEnter(code, expected); } @@ -1712,17 +2034,21 @@ void M() {}"" public void Command_Class() { var code = -@"class C -{$$ -}"; + """ + class C + {$$ + } + """; var expected = -@"/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// $$ + /// + class C + { + } + """; VerifyInsertCommentCommand(code, expected); } @@ -1733,10 +2059,12 @@ public void Command_Record() var code = "record R$$;"; var expected = -@"/// -/// $$ -/// -record R;"; + """ + /// + /// $$ + /// + record R; + """; VerifyInsertCommentCommand(code, expected); } @@ -1747,10 +2075,12 @@ public void Command_RecordStruct() var code = "record struct R$$;"; var expected = -@"/// -/// $$ -/// -record struct R;"; + """ + /// + /// $$ + /// + record struct R; + """; VerifyInsertCommentCommand(code, expected); } @@ -1761,12 +2091,14 @@ public void Command_RecordWithPositionalParameters() var code = "record R$$(string S, int I);"; var expected = -@"/// -/// $$ -/// -/// -/// -record R(string S, int I);"; + """ + /// + /// $$ + /// + /// + /// + record R(string S, int I); + """; VerifyInsertCommentCommand(code, expected); } @@ -1777,12 +2109,14 @@ public void Command_ClassParameters() var code = "class R$$(string S, int I);"; var expected = -@"/// -/// $$ -/// -/// -/// -class R(string S, int I);"; + """ + /// + /// $$ + /// + /// + /// + class R(string S, int I); + """; VerifyInsertCommentCommand(code, expected); } @@ -1793,12 +2127,14 @@ public void Command_RecordStructWithPositionalParameters() var code = "record struct R$$(string S, int I);"; var expected = -@"/// -/// $$ -/// -/// -/// -record struct R(string S, int I);"; + """ + /// + /// $$ + /// + /// + /// + record struct R(string S, int I); + """; VerifyInsertCommentCommand(code, expected); } @@ -1809,12 +2145,14 @@ public void Command_StructParameters() var code = "struct R$$(string S, int I);"; var expected = -@"/// -/// $$ -/// -/// -/// -struct R(string S, int I);"; + """ + /// + /// $$ + /// + /// + /// + struct R(string S, int I); + """; VerifyInsertCommentCommand(code, expected); } @@ -1823,17 +2161,21 @@ struct R(string S, int I);"; public void Command_Class_AutoGenerateXmlDocCommentsOff() { var code = -@"class C -{$$ -}"; + """ + class C + {$$ + } + """; var expected = -@"/// -/// $$ -/// -class C -{ -}"; + """ + /// + /// $$ + /// + class C + { + } + """; VerifyInsertCommentCommand(code, expected, globalOptions: new OptionsCollection(LanguageNames.CSharp) { @@ -1845,14 +2187,18 @@ class C public void Command_BeforeClass1() { var code = -@"$$ -class C { }"; + """ + $$ + class C { } + """; var expected = -@" -/// -/// $$ -/// -class C { }"; + """ + + /// + /// $$ + /// + class C { } + """; VerifyInsertCommentCommand(code, expected); } @@ -1861,16 +2207,20 @@ class C { }"; public void Command_BeforeClass2() { var code = -@"class B { } -$$ -class C { }"; + """ + class B { } + $$ + class C { } + """; var expected = -@"class B { } + """ + class B { } -/// -/// $$ -/// -class C { }"; + /// + /// $$ + /// + class C { } + """; VerifyInsertCommentCommand(code, expected); } @@ -1879,20 +2229,24 @@ class C { }"; public void Command_BeforeClass3() { var code = -@"class B -{ - $$ - class C { } -}"; - var expected = -@"class B -{ - - /// - /// $$ - /// - class C { } -}"; + """ + class B + { + $$ + class C { } + } + """; + var expected = + """ + class B + { + + /// + /// $$ + /// + class C { } + } + """; VerifyInsertCommentCommand(code, expected); } @@ -1901,14 +2255,18 @@ class C { } public void Command_Class_NotIfMultilineDocCommentExists() { var code = -@"/** -*/ -class C { $$ }"; + """ + /** + */ + class C { $$ } + """; var expected = -@"/** -*/ -class C { $$ }"; + """ + /** + */ + class C { $$ } + """; VerifyInsertCommentCommand(code, expected); } @@ -1916,22 +2274,26 @@ class C { $$ }"; public void Command_Method() { var code = -@"class C -{ - int M(int goo) { $$return 0; } -}"; + """ + class C + { + int M(int goo) { $$return 0; } + } + """; var expected = -@"class C -{ - /// - /// $$ - /// - /// - /// - /// - int M(int goo) { return 0; } -}"; + """ + class C + { + /// + /// $$ + /// + /// + /// + /// + int M(int goo) { return 0; } + } + """; VerifyInsertCommentCommand(code, expected); } @@ -1940,16 +2302,20 @@ public void Command_Method() public void Command_Class_NotIfCommentExists() { var code = -@"/// -class C -{$$ -}"; + """ + /// + class C + {$$ + } + """; var expected = -@"/// -class C -{$$ -}"; + """ + /// + class C + {$$ + } + """; VerifyInsertCommentCommand(code, expected); } @@ -1958,18 +2324,22 @@ class C public void Command_Method_NotIfCommentExists() { var code = -@"class C -{ - /// - int M(int goo) { $$return 0; } -}"; + """ + class C + { + /// + int M(int goo) { $$return 0; } + } + """; var expected = -@"class C -{ - /// - int M(int goo) { $$return 0; } -}"; + """ + class C + { + /// + int M(int goo) { $$return 0; } + } + """; VerifyInsertCommentCommand(code, expected); } @@ -1980,10 +2350,12 @@ public void Command_FirstClassOnLine() var code = @"$$class C { } class D { }"; var expected = -@"/// -/// $$ -/// -class C { } class D { }"; + """ + /// + /// $$ + /// + class C { } class D { } + """; VerifyInsertCommentCommand(code, expected); } @@ -2002,19 +2374,23 @@ public void Command_NotOnSecondClassOnLine() public void Command_FirstMethodOnLine() { var code = -@"class C -{ - protected abstract void $$Goo(); protected abstract void Bar(); -}"; + """ + class C + { + protected abstract void $$Goo(); protected abstract void Bar(); + } + """; var expected = -@"class C -{ - /// - /// $$ - /// - protected abstract void Goo(); protected abstract void Bar(); -}"; + """ + class C + { + /// + /// $$ + /// + protected abstract void Goo(); protected abstract void Bar(); + } + """; VerifyInsertCommentCommand(code, expected); } @@ -2023,16 +2399,20 @@ public void Command_FirstMethodOnLine() public void Command_NotOnSecondMethodOnLine() { var code = -@"class C -{ - protected abstract void Goo(); protected abstract void $$Bar(); -}"; + """ + class C + { + protected abstract void Goo(); protected abstract void $$Bar(); + } + """; var expected = -@"class C -{ - protected abstract void Goo(); protected abstract void $$Bar(); -}"; + """ + class C + { + protected abstract void Goo(); protected abstract void $$Bar(); + } + """; VerifyInsertCommentCommand(code, expected); } @@ -2041,28 +2421,32 @@ public void Command_NotOnSecondMethodOnLine() public void TestUseTab() { var code = -@"using System; + """ + using System; -public class Class1 -{ - //$$ - public Class1() - { - } -}"; + public class Class1 + { + //$$ + public Class1() + { + } + } + """; var expected = -@"using System; + """ + using System; -public class Class1 -{ - /// - /// $$ - /// - public Class1() - { - } -}"; + public class Class1 + { + /// + /// $$ + /// + public Class1() + { + } + } + """; VerifyTypingCharacter(code, expected, useTabs: true); } @@ -2071,27 +2455,31 @@ public Class1() public void TestOpenLineAbove1() { const string code = -@"class C -{ - /// - /// stuff$$ - /// - void M() - { - } -}"; - - var expected = -@"class C -{ - /// - /// $$ - /// stuff - /// - void M() - { - } -}"; + """ + class C + { + /// + /// stuff$$ + /// + void M() + { + } + } + """; + + var expected = + """ + class C + { + /// + /// $$ + /// stuff + /// + void M() + { + } + } + """; VerifyOpenLineAbove(code, expected); } @@ -2100,27 +2488,31 @@ void M() public void TestOpenLineAbove2() { const string code = -@"class C -{ - /// - /// $$stuff - /// - void M() - { - } -}"; + """ + class C + { + /// + /// $$stuff + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// $$ - /// stuff - /// - void M() - { - } -}"; + """ + class C + { + /// + /// $$ + /// stuff + /// + void M() + { + } + } + """; VerifyOpenLineAbove(code, expected); } @@ -2129,29 +2521,33 @@ void M() public void TestOpenLineAbove3() { const string code = -@"class C -{ - /// $$ - /// stuff - /// - void M() - { - } -}"; + """ + class C + { + /// $$ + /// stuff + /// + void M() + { + } + } + """; // Note that the caret position specified below does not look correct because // it is in virtual space in this case. const string expected = -@"class C -{ -$$ - /// - /// stuff - /// - void M() - { - } -}"; + """ + class C + { + $$ + /// + /// stuff + /// + void M() + { + } + } + """; VerifyOpenLineAbove(code, expected); } @@ -2160,27 +2556,31 @@ void M() public void TestOpenLineAbove4_Tabs() { const string code = -@"class C -{ - /// - /// $$stuff - /// - void M() - { - } -}"; + """ + class C + { + /// + /// $$stuff + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// $$ - /// stuff - /// - void M() - { - } -}"; + """ + class C + { + /// + /// $$ + /// stuff + /// + void M() + { + } + } + """; VerifyOpenLineAbove(code, expected, useTabs: true); } @@ -2189,27 +2589,31 @@ void M() public void TestOpenLineBelow1() { const string code = -@"class C -{ - /// - /// stuff$$ - /// - void M() - { - } -}"; + """ + class C + { + /// + /// stuff$$ + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// stuff - /// $$ - /// - void M() - { - } -}"; + """ + class C + { + /// + /// stuff + /// $$ + /// + void M() + { + } + } + """; VerifyOpenLineBelow(code, expected); } @@ -2218,27 +2622,31 @@ void M() public void TestOpenLineBelow2() { const string code = -@"class C -{ - /// - /// $$stuff - /// - void M() - { - } -}"; + """ + class C + { + /// + /// $$stuff + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// stuff - /// $$ - /// - void M() - { - } -}"; + """ + class C + { + /// + /// stuff + /// $$ + /// + void M() + { + } + } + """; VerifyOpenLineBelow(code, expected); } @@ -2247,17 +2655,21 @@ void M() public void TestOpenLineBelow3() { const string code = -@"/// -/// stuff -/// $$ -"; + """ + /// + /// stuff + /// $$ + + """; const string expected = -@"/// -/// stuff -/// -/// $$ -"; + """ + /// + /// stuff + /// + /// $$ + + """; VerifyOpenLineBelow(code, expected); } @@ -2266,27 +2678,31 @@ public void TestOpenLineBelow3() public void TestOpenLineBelow4_Tabs() { const string code = -@"class C -{ - /// - /// $$stuff - /// - void M() - { - } -}"; + """ + class C + { + /// + /// $$stuff + /// + void M() + { + } + } + """; const string expected = -@"class C -{ - /// - /// stuff - /// $$ - /// - void M() - { - } -}"; + """ + class C + { + /// + /// stuff + /// $$ + /// + void M() + { + } + } + """; VerifyOpenLineBelow(code, expected, useTabs: true); } @@ -2295,17 +2711,21 @@ void M() public void VerifyEnterWithTrimNewLineEditorConfigOption() { const string code = -@"/// -/// $$ -/// -class C { }"; + """ + /// + /// $$ + /// + class C { } + """; const string expected = -@"/// -/// -/// $$ -/// -class C { }"; + """ + /// + /// + /// $$ + /// + class C { } + """; VerifyPressingEnter(code, expected, useTabs: true, trimTrailingWhiteSpace: true); } @@ -2314,18 +2734,22 @@ class C { }"; public void TypingCharacter_Class_WithComment() { var code = -@"//$$ This is my class and it does great things. -class C -{ -}"; + """ + //$$ This is my class and it does great things. + class C + { + } + """; var expected = -@"/// -/// $$This is my class and it does great things. -/// -class C -{ -}"; + """ + /// + /// $$This is my class and it does great things. + /// + class C + { + } + """; VerifyTypingCharacter(code, expected); } @@ -2334,18 +2758,22 @@ class C public void TypingCharacter_Class_WithComment_NoSpace() { var code = -@"//$$This is my class and it does great things. -class C -{ -}"; + """ + //$$This is my class and it does great things. + class C + { + } + """; var expected = -@"/// -/// $$This is my class and it does great things. -/// -class C -{ -}"; + """ + /// + /// $$This is my class and it does great things. + /// + class C + { + } + """; VerifyTypingCharacter(code, expected); } diff --git a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs index e17ddcc4dd94c..fc5e10a37f257 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs @@ -627,20 +627,14 @@ private void Method() } [Theory] - [InlineData(LanguageNames.CSharp, 50)] - [InlineData(LanguageNames.VisualBasic, 87)] - public void VerifyAllCodeStyleFixersAreSupportedByCodeCleanup(string language, int numberOfUnsupportedDiagnosticIds) + [InlineData(LanguageNames.CSharp)] + [InlineData(LanguageNames.VisualBasic)] + public void VerifyAllCodeStyleFixersAreSupportedByCodeCleanup(string language) { var supportedDiagnostics = GetSupportedDiagnosticIdsForCodeCleanupService(language); // No Duplicates Assert.Equal(supportedDiagnostics, supportedDiagnostics.Distinct()); - - // Exact Number of Unsupported Diagnostic Ids - var ideDiagnosticIds = typeof(IDEDiagnosticIds).GetFields().Select(f => f.GetValue(f) as string).ToArray(); - var unsupportedDiagnosticIds = ideDiagnosticIds.Except(supportedDiagnostics).ToArray(); - - Assert.Equal(numberOfUnsupportedDiagnosticIds, unsupportedDiagnosticIds.Length); } private const string _code = """ diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs index e63654db9546d..983b7fa7327c6 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs @@ -239,15 +239,19 @@ private static string GetFormattedIDEAnalyzerTitle(int ideDiagnosticId, string n protected static Task TestInClassAsync(string code, string expectedDescription, params TextSpan[] relatedSpans) => TestAsync( - """ + $$""" class C { - """ + code + "}", expectedDescription, relatedSpans.ToImmutableArray()); + {{code}} + } + """, expectedDescription, relatedSpans.ToImmutableArray()); protected static Task TestInMethodAsync(string code, string expectedDescription, params TextSpan[] relatedSpans) => TestInClassAsync( - """ + $$""" void M() { - """ + code + "}", expectedDescription, relatedSpans); + {{code}} + } + """, expectedDescription, relatedSpans); } diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index f65586109000d..e6fc8b85e3e7e 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Security; using System.Threading; @@ -23,15 +24,21 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.QuickInfo; [Trait(Traits.Feature, Traits.Features.QuickInfo)] -public class SemanticQuickInfoSourceTests : AbstractSemanticQuickInfoSourceTests +public sealed class SemanticQuickInfoSourceTests : AbstractSemanticQuickInfoSourceTests { - private static async Task TestWithOptionsAsync(CSharpParseOptions options, string markup, params Action[] expectedResults) + private static async Task TestWithOptionsAsync( + CSharpParseOptions options, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + params Action[] expectedResults) { using var workspace = EditorTestWorkspace.CreateCSharp(markup, options); await TestWithOptionsAsync(workspace, expectedResults); } - private static async Task TestWithOptionsAsync(CSharpCompilationOptions options, string markup, params Action[] expectedResults) + private static async Task TestWithOptionsAsync( + CSharpCompilationOptions options, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + params Action[] expectedResults) { using var workspace = EditorTestWorkspace.CreateCSharp(markup, compilationOptions: options); await TestWithOptionsAsync(workspace, expectedResults); @@ -83,7 +90,9 @@ private static async Task TestWithOptionsAsync(Document document, QuickInfoServi } } - private static async Task VerifyWithMscorlib45Async(string markup, params Action[] expectedResults) + private static async Task VerifyWithMscorlib45Async( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + params Action[] expectedResults) { var xmlString = string.Format(""" @@ -98,7 +107,9 @@ private static async Task VerifyWithMscorlib45Async(string markup, params Action await VerifyWithMarkupAsync(xmlString, expectedResults); } - private static async Task VerifyWithNet8Async(string markup, params Action[] expectedResults) + private static async Task VerifyWithNet8Async( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + params Action[] expectedResults) { var xmlString = string.Format(""" @@ -141,42 +152,56 @@ private static async Task VerifyWithMarkupAsync(string xmlString, Action[] expectedResults) + protected override async Task TestAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + params Action[] expectedResults) { await TestWithOptionsAsync(Options.Regular, markup, expectedResults); await TestWithOptionsAsync(Options.Script, markup, expectedResults); } - private async Task TestWithUsingsAsync(string markup, params Action[] expectedResults) + private async Task TestWithUsingsAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + params Action[] expectedResults) { var markupWithUsings = -@"using System; -using System.Collections.Generic; -using System.Linq; -" + markup; + """ + using System; + using System.Collections.Generic; + using System.Linq; + + """ + markup; await TestAsync(markupWithUsings, expectedResults); } - private Task TestInClassAsync(string markup, params Action[] expectedResults) + private Task TestInClassAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + params Action[] expectedResults) { var markupInClass = "class C { " + markup + " }"; return TestWithUsingsAsync(markupInClass, expectedResults); } - private Task TestInMethodAsync(string markup, params Action[] expectedResults) + private Task TestInMethodAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + params Action[] expectedResults) { var markupInMethod = "class C { void M() { " + markup + " } }"; return TestWithUsingsAsync(markupInMethod, expectedResults); } - private Task TestInMethodAsync(string markup, string extraSource, params Action[] expectedResults) + private Task TestInMethodAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string extraSource, + params Action[] expectedResults) { var markupInMethod = "class C { void M() { " + markup + " } }" + extraSource; return TestWithUsingsAsync(markupInMethod, expectedResults); } - private static async Task TestWithReferenceAsync(string sourceCode, + private static async Task TestWithReferenceAsync( + string sourceCode, string referencedCode, string sourceLanguage, string referencedLanguage, @@ -199,19 +224,20 @@ private static async Task TestWithMetadataReferenceHelperAsync( string referencedLanguage, params Action[] expectedResults) { - var xmlString = string.Format(@" - - - -{1} - - - -{3} - - - -", sourceLanguage, SecurityElement.Escape(sourceCode), + var xmlString = string.Format(""" + + + + {1} + + + + {3} + + + + + """, sourceLanguage, SecurityElement.Escape(sourceCode), referencedLanguage, SecurityElement.Escape(referencedCode)); await VerifyWithReferenceWorkerAsync(xmlString, expectedResults); @@ -224,21 +250,22 @@ private static async Task TestWithProjectReferenceHelperAsync( string referencedLanguage, params Action[] expectedResults) { - var xmlString = string.Format(@" - - - ReferencedProject - -{1} - - - - -{3} - - - -", sourceLanguage, SecurityElement.Escape(sourceCode), + var xmlString = string.Format(""" + + + ReferencedProject + + {1} + + + + + {3} + + + + + """, sourceLanguage, SecurityElement.Escape(sourceCode), referencedLanguage, SecurityElement.Escape(referencedCode)); await VerifyWithReferenceWorkerAsync(xmlString, expectedResults); @@ -250,17 +277,18 @@ private static async Task TestInSameProjectHelperAsync( string sourceLanguage, params Action[] expectedResults) { - var xmlString = string.Format(@" - - - -{1} - - -{2} - - -", sourceLanguage, SecurityElement.Escape(sourceCode), SecurityElement.Escape(referencedCode)); + var xmlString = string.Format(""" + + + + {1} + + + {2} + + + + """, sourceLanguage, SecurityElement.Escape(sourceCode), SecurityElement.Escape(referencedCode)); await VerifyWithReferenceWorkerAsync(xmlString, expectedResults); } @@ -293,7 +321,7 @@ private static async Task VerifyWithReferenceWorkerAsync(string xmlString, param } } - protected async Task TestInvalidTypeInClassAsync(string code) + private async Task TestInvalidTypeInClassAsync(string code) { var codeInClass = "class C { " + code + " }"; await TestAsync(codeInClass); @@ -303,7 +331,7 @@ protected async Task TestInvalidTypeInClassAsync(string code) public async Task TestNamespaceInUsingDirective() { await TestAsync( -@"using $$System;", + @"using $$System;", MainDescription("namespace System")); } @@ -311,7 +339,7 @@ await TestAsync( public async Task TestNamespaceInUsingDirective2() { await TestAsync( -@"using System.Coll$$ections.Generic;", + @"using System.Coll$$ections.Generic;", MainDescription("namespace System.Collections")); } @@ -319,7 +347,7 @@ await TestAsync( public async Task TestNamespaceInUsingDirective3() { await TestAsync( -@"using System.L$$inq;", + @"using System.L$$inq;", MainDescription("namespace System.Linq")); } @@ -327,7 +355,7 @@ await TestAsync( public async Task TestNamespaceInUsingDirectiveWithAlias() { await TestAsync( -@"using Goo = Sys$$tem.Console;", + @"using Goo = Sys$$tem.Console;", MainDescription("namespace System")); } @@ -335,7 +363,7 @@ await TestAsync( public async Task TestTypeInUsingDirectiveWithAlias() { await TestAsync( -@"using Goo = System.Con$$sole;", + @"using Goo = System.Con$$sole;", MainDescription("class System.Console")); } @@ -343,9 +371,11 @@ await TestAsync( public async Task TestDocumentationInUsingDirectiveWithAlias() { var markup = -@"using I$$ = IGoo; -///summary for interface IGoo -interface IGoo { }"; + """ + using I$$ = IGoo; + ///summary for interface IGoo + interface IGoo { } + """; await TestAsync(markup, MainDescription("interface IGoo"), @@ -356,10 +386,12 @@ await TestAsync(markup, public async Task TestDocumentationInUsingDirectiveWithAlias2() { var markup = -@"using I = IGoo; -///summary for interface IGoo -interface IGoo { } -class C : I$$ { }"; + """ + using I = IGoo; + ///summary for interface IGoo + interface IGoo { } + class C : I$$ { } + """; await TestAsync(markup, MainDescription("interface IGoo"), @@ -370,13 +402,15 @@ await TestAsync(markup, public async Task TestDocumentationInUsingDirectiveWithAlias3() { var markup = -@"using I = IGoo; -///summary for interface IGoo -interface IGoo -{ - void Goo(); -} -class C : I$$ { }"; + """ + using I = IGoo; + ///summary for interface IGoo + interface IGoo + { + void Goo(); + } + class C : I$$ { } + """; await TestAsync(markup, MainDescription("interface IGoo"), @@ -387,9 +421,11 @@ await TestAsync(markup, public async Task TestThis() { var markup = -@" -///summary for Class C -class C { string M() { return thi$$s.ToString(); } }"; + """ + + ///summary for Class C + class C { string M() { return thi$$s.ToString(); } } + """; await TestWithUsingsAsync(markup, MainDescription("class C"), @@ -400,9 +436,11 @@ await TestWithUsingsAsync(markup, public async Task TestClassWithDocComment() { var markup = -@" -///Hello! -class C { void M() { $$C obj; } }"; + """ + + ///Hello! + class C { void M() { $$C obj; } } + """; await TestAsync(markup, MainDescription("class C"), @@ -416,60 +454,72 @@ public async Task TestSingleLineDocComments() // SingleLine doc comment with leading whitespace await TestAsync( -@"///Hello! -class C -{ - void M() - { - $$C obj; - } -}", + """ + ///Hello! + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // SingleLine doc comment with space before opening tag await TestAsync( -@"/// Hello! -class C -{ - void M() - { - $$C obj; - } -}", + """ + /// Hello! + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // SingleLine doc comment with space before opening tag and leading whitespace await TestAsync( -@"/// Hello! -class C -{ - void M() - { - $$C obj; - } -}", + """ + /// Hello! + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // SingleLine doc comment with leading whitespace and blank line await TestAsync( -@"///Hello! -/// + """ + ///Hello! + /// -class C -{ - void M() - { - $$C obj; - } -}", + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // SingleLine doc comment with '\r' line separators - await TestAsync("///Hello!\r///\rclass C { void M() { $$C obj; } }", + await TestAsync(""" + ///Hello! + /// + class C { void M() { $$C obj; } } + """, MainDescription("class C"), Documentation("Hello!")); } @@ -481,97 +531,116 @@ public async Task TestMultiLineDocComments() // Multiline doc comment with leading whitespace await TestAsync( -@"/**Hello!*/ -class C -{ - void M() - { - $$C obj; - } -}", + """ + /**Hello!*/ + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // Multiline doc comment with space before opening tag await TestAsync( -@"/** Hello! - **/ -class C -{ - void M() - { - $$C obj; - } -}", + """ + /** Hello! + **/ + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // Multiline doc comment with space before opening tag and leading whitespace await TestAsync( -@"/** - ** Hello! - **/ -class C -{ - void M() - { - $$C obj; - } -}", + """ + /** + ** Hello! + **/ + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // Multiline doc comment with no per-line prefix await TestAsync( -@"/** - - Hello! - -*/ -class C -{ - void M() - { - $$C obj; - } -}", + """ + /** + + Hello! + + */ + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // Multiline doc comment with inconsistent per-line prefix await TestAsync( -@"/** - ** - Hello! - ** - **/ -class C -{ - void M() - { - $$C obj; - } -}", + """ + /** + ** + Hello! + ** + **/ + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // Multiline doc comment with closing comment on final line await TestAsync( -@"/** -Hello! -*/ -class C -{ - void M() - { - $$C obj; - } -}", + """ + /** + Hello! + */ + class C + { + void M() + { + $$C obj; + } + } + """, MainDescription("class C"), Documentation("Hello!")); // Multiline doc comment with '\r' line separators - await TestAsync("/**\r* \r* Hello!\r* \r*/\rclass C { void M() { $$C obj; } }", + await TestAsync(""" + /** + * + * Hello! + * + */ + class C { void M() { $$C obj; } } + """, MainDescription("class C"), Documentation("Hello!")); } @@ -580,9 +649,11 @@ void M() public async Task TestMethodWithDocComment() { var markup = -@" -///Hello! -void M() { M$$() }"; + """ + + ///Hello! + void M() { M$$() } + """; await TestInClassAsync(markup, MainDescription("void C.M()"), @@ -593,7 +664,7 @@ await TestInClassAsync(markup, public async Task TestInt32() { await TestInClassAsync( -@"$$Int32 i;", + @"$$Int32 i;", MainDescription("struct System.Int32")); } @@ -601,7 +672,7 @@ await TestInClassAsync( public async Task TestBuiltInInt() { await TestInClassAsync( -@"$$int i;", + @"$$int i;", MainDescription("struct System.Int32")); } @@ -609,7 +680,7 @@ await TestInClassAsync( public async Task TestString() { await TestInClassAsync( -@"$$String s;", + @"$$String s;", MainDescription("class System.String")); } @@ -617,7 +688,7 @@ await TestInClassAsync( public async Task TestBuiltInString() { await TestInClassAsync( -@"$$string s;", + @"$$string s;", MainDescription("class System.String")); } @@ -625,7 +696,7 @@ await TestInClassAsync( public async Task TestBuiltInStringAtEndOfToken() { await TestInClassAsync( -@"string$$ s;", + @"string$$ s;", MainDescription("class System.String")); } @@ -633,7 +704,7 @@ await TestInClassAsync( public async Task TestBoolean() { await TestInClassAsync( -@"$$Boolean b;", + @"$$Boolean b;", MainDescription("struct System.Boolean")); } @@ -641,7 +712,7 @@ await TestInClassAsync( public async Task TestBuiltInBool() { await TestInClassAsync( -@"$$bool b;", + @"$$bool b;", MainDescription("struct System.Boolean")); } @@ -649,7 +720,7 @@ await TestInClassAsync( public async Task TestSingle() { await TestInClassAsync( -@"$$Single s;", + @"$$Single s;", MainDescription("struct System.Single")); } @@ -657,7 +728,7 @@ await TestInClassAsync( public async Task TestBuiltInFloat() { await TestInClassAsync( -@"$$float f;", + @"$$float f;", MainDescription("struct System.Single")); } @@ -665,63 +736,75 @@ await TestInClassAsync( public async Task TestVoidIsInvalid() { await TestInvalidTypeInClassAsync( -@"$$void M() -{ -}"); + """ + $$void M() + { + } + """); } [Fact] public async Task TestInvalidPointer1_931958() { await TestInvalidTypeInClassAsync( -@"$$T* i;"); + @"$$T* i;"); } [Fact] public async Task TestInvalidPointer2_931958() { await TestInvalidTypeInClassAsync( -@"T$$* i;"); + @"T$$* i;"); } [Fact] public async Task TestInvalidPointer3_931958() { await TestInvalidTypeInClassAsync( -@"T*$$ i;"); + @"T*$$ i;"); } [Fact] public async Task TestListOfString() { await TestInClassAsync( -@"$$List l;", + @"$$List l;", MainDescription("class System.Collections.Generic.List"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} string")); + TypeParameterMap($""" + + T {FeaturesResources.is_} string + """)); } [Fact] public async Task TestListOfSomethingFromSource() { var markup = -@" -///Generic List -public class GenericList { Generic$$List t; }"; + """ + + ///Generic List + public class GenericList { Generic$$List t; } + """; await TestAsync(markup, MainDescription("class GenericList"), Documentation("Generic List"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} int")); + TypeParameterMap($""" + + T {FeaturesResources.is_} int + """)); } [Fact] public async Task TestListOfT() { await TestInMethodAsync( -@"class C -{ - $$List l; -}", + """ + class C + { + $$List l; + } + """, MainDescription("class System.Collections.Generic.List")); } @@ -729,10 +812,13 @@ await TestInMethodAsync( public async Task TestDictionaryOfIntAndString() { await TestInClassAsync( -@"$$Dictionary d;", + @"$$Dictionary d;", MainDescription("class System.Collections.Generic.Dictionary"), TypeParameterMap( - Lines($"\r\nTKey {FeaturesResources.is_} int", + Lines($""" + + TKey {FeaturesResources.is_} int + """, $"TValue {FeaturesResources.is_} string"))); } @@ -740,13 +826,18 @@ await TestInClassAsync( public async Task TestDictionaryOfTAndU() { await TestInMethodAsync( -@"class C -{ - $$Dictionary d; -}", + """ + class C + { + $$Dictionary d; + } + """, MainDescription("class System.Collections.Generic.Dictionary"), TypeParameterMap( - Lines($"\r\nTKey {FeaturesResources.is_} T", + Lines($""" + + TKey {FeaturesResources.is_} T + """, $"TValue {FeaturesResources.is_} U"))); } @@ -754,19 +845,24 @@ await TestInMethodAsync( public async Task TestIEnumerableOfInt() { await TestInClassAsync( -@"$$IEnumerable M() -{ - yield break; -}", + """ + $$IEnumerable M() + { + yield break; + } + """, MainDescription("interface System.Collections.Generic.IEnumerable"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} int")); + TypeParameterMap($""" + + T {FeaturesResources.is_} int + """)); } [Fact] public async Task TestEventHandler() { await TestInClassAsync( -@"event $$EventHandler e;", + @"event $$EventHandler e;", MainDescription("delegate void System.EventHandler(object sender, System.EventArgs e)")); } @@ -774,10 +870,12 @@ await TestInClassAsync( public async Task TestTypeParameter() { await TestAsync( -@"class C -{ - $$T t; -}", + """ + class C + { + $$T t; + } + """, MainDescription($"T {FeaturesResources.in_} C")); } @@ -785,10 +883,12 @@ await TestAsync( public async Task TestTypeParameterWithDocComment() { var markup = -@" -///Hello! -///T is Type Parameter -class C { $$T t; }"; + """ + + ///Hello! + ///T is Type Parameter + class C { $$T t; } + """; await TestAsync(markup, MainDescription($"T {FeaturesResources.in_} C"), @@ -799,10 +899,12 @@ await TestAsync(markup, public async Task TestTypeParameter1_Bug931949() { await TestAsync( -@"class T1 -{ - $$T11 t; -}", + """ + class T1 + { + $$T11 t; + } + """, MainDescription($"T11 {FeaturesResources.in_} T1")); } @@ -810,10 +912,12 @@ await TestAsync( public async Task TestTypeParameter2_Bug931949() { await TestAsync( -@"class T1 -{ - T$$11 t; -}", + """ + class T1 + { + T$$11 t; + } + """, MainDescription($"T11 {FeaturesResources.in_} T1")); } @@ -821,10 +925,12 @@ await TestAsync( public async Task TestTypeParameter3_Bug931949() { await TestAsync( -@"class T1 -{ - T1$$1 t; -}", + """ + class T1 + { + T1$$1 t; + } + """, MainDescription($"T11 {FeaturesResources.in_} T1")); } @@ -832,10 +938,12 @@ await TestAsync( public async Task TestTypeParameter4_Bug931949() { await TestAsync( -@"class T1 -{ - T11$$ t; -}", + """ + class T1 + { + T11$$ t; + } + """, MainDescription($"T11 {FeaturesResources.in_} T1")); } @@ -844,20 +952,25 @@ public async Task TestNullableOfInt() { await TestInClassAsync(@"$$Nullable i; }", MainDescription("struct System.Nullable where T : struct"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} int")); + TypeParameterMap($""" + + T {FeaturesResources.is_} int + """)); } [Fact] public async Task TestGenericTypeDeclaredOnMethod1_Bug1946() { await TestAsync( -@"class C -{ - static void Meth1($$T1 i) where T1 : struct - { - T1 i; - } -}", + """ + class C + { + static void Meth1($$T1 i) where T1 : struct + { + T1 i; + } + } + """, MainDescription($"T1 {FeaturesResources.in_} C.Meth1 where T1 : struct")); } @@ -865,13 +978,15 @@ static void Meth1($$T1 i) where T1 : struct public async Task TestGenericTypeDeclaredOnMethod2_Bug1946() { await TestAsync( -@"class C -{ - static void Meth1(T1 i) where $$T1 : struct - { - T1 i; - } -}", + """ + class C + { + static void Meth1(T1 i) where $$T1 : struct + { + T1 i; + } + } + """, MainDescription($"T1 {FeaturesResources.in_} C.Meth1 where T1 : struct")); } @@ -879,13 +994,15 @@ static void Meth1(T1 i) where $$T1 : struct public async Task TestGenericTypeDeclaredOnMethod3_Bug1946() { await TestAsync( -@"class C -{ - static void Meth1(T1 i) where T1 : struct - { - $$T1 i; - } -}", + """ + class C + { + static void Meth1(T1 i) where T1 : struct + { + $$T1 i; + } + } + """, MainDescription($"T1 {FeaturesResources.in_} C.Meth1 where T1 : struct")); } @@ -893,19 +1010,23 @@ static void Meth1(T1 i) where T1 : struct public async Task TestGenericTypeParameterConstraint_Class() { await TestAsync( -@"class C where $$T : class -{ -}", - MainDescription($"T {FeaturesResources.in_} C where T : class")); - } + """ + class C where $$T : class + { + } + """, + MainDescription($"T {FeaturesResources.in_} C where T : class")); + } [Fact] public async Task TestGenericTypeParameterConstraint_Struct() { await TestAsync( -@"struct S where $$T : class -{ -}", + """ + struct S where $$T : class + { + } + """, MainDescription($"T {FeaturesResources.in_} S where T : class")); } @@ -913,9 +1034,11 @@ await TestAsync( public async Task TestGenericTypeParameterConstraint_Interface() { await TestAsync( -@"interface I where $$T : class -{ -}", + """ + interface I where $$T : class + { + } + """, MainDescription($"T {FeaturesResources.in_} I where T : class")); } @@ -923,7 +1046,7 @@ await TestAsync( public async Task TestGenericTypeParameterConstraint_Delegate() { await TestAsync( -@"delegate void D() where $$T : class;", + @"delegate void D() where $$T : class;", MainDescription($"T {FeaturesResources.in_} D where T : class")); } @@ -945,13 +1068,15 @@ await TestAsync(@"class C where $$T : System.Collections.Generic.IEnumerable< public async Task TestMethodReferenceInSameMethod() { await TestAsync( -@"class C -{ - void M() - { - M$$(); - } -}", + """ + class C + { + void M() + { + M$$(); + } + } + """, MainDescription("void C.M()")); } @@ -959,9 +1084,11 @@ void M() public async Task TestMethodReferenceInSameMethodWithDocComment() { var markup = -@" -///Hello World -void M() { M$$(); }"; + """ + + ///Hello World + void M() { M$$(); } + """; await TestInClassAsync(markup, MainDescription("void C.M()"), @@ -972,12 +1099,14 @@ await TestInClassAsync(markup, public async Task TestFieldInMethodBuiltIn() { var markup = -@"int field; + """ + int field; -void M() -{ - field$$ -}"; + void M() + { + field$$ + } + """; await TestInClassAsync(markup, MainDescription($"({FeaturesResources.field}) int C.field")); @@ -987,12 +1116,14 @@ await TestInClassAsync(markup, public async Task TestFieldInMethodBuiltIn2() { await TestInClassAsync( -@"int field; + """ + int field; -void M() -{ - int f = field$$; -}", + void M() + { + int f = field$$; + } + """, MainDescription($"({FeaturesResources.field}) int C.field")); } @@ -1000,21 +1131,25 @@ void M() public async Task TestFieldInMethodBuiltInWithFieldInitializer() { await TestInClassAsync( -@"int field = 1; + """ + int field = 1; -void M() -{ - int f = field $$; -}"); + void M() + { + int f = field $$; + } + """); } [Fact] public async Task TestOperatorBuiltIn() { await TestInMethodAsync( -@"int x; + """ + int x; -x = x$$+1;", + x = x$$+1; + """, MainDescription("int int.operator +(int left, int right)")); } @@ -1022,9 +1157,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn1() { await TestInMethodAsync( -@"int x; + """ + int x; -x = x$$ + 1;", + x = x$$ + 1; + """, MainDescription($"({FeaturesResources.local_variable}) int x")); } @@ -1032,9 +1169,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn2() { await TestInMethodAsync( -@"int x; + """ + int x; -x = x+$$x;", + x = x+$$x; + """, MainDescription($"({FeaturesResources.local_variable}) int x")); } @@ -1042,9 +1181,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn3() { await TestInMethodAsync( -@"int x; + """ + int x; -x = x +$$ x;", + x = x +$$ x; + """, MainDescription("int int.operator +(int left, int right)")); } @@ -1052,9 +1193,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn4() { await TestInMethodAsync( -@"int x; + """ + int x; -x = x + $$x;", + x = x + $$x; + """, MainDescription($"({FeaturesResources.local_variable}) int x")); } @@ -1062,9 +1205,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn5() { await TestInMethodAsync( -@"int x; + """ + int x; -x = unchecked (x$$+1);", + x = unchecked (x$$+1); + """, MainDescription("int int.operator +(int left, int right)")); } @@ -1072,9 +1217,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn6() { await TestInMethodAsync( -@"int x; + """ + int x; -x = checked (x$$+1);", + x = checked (x$$+1); + """, MainDescription("int int.operator checked +(int left, int right)")); } @@ -1082,9 +1229,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn7() { await TestInMethodAsync( -@"int x; + """ + int x; -x = unchecked (x +$$ x);", + x = unchecked (x +$$ x); + """, MainDescription("int int.operator +(int left, int right)")); } @@ -1092,9 +1241,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn8() { await TestInMethodAsync( -@"int x; + """ + int x; -x = checked (x +$$ x);", + x = checked (x +$$ x); + """, MainDescription("int int.operator checked +(int left, int right)")); } @@ -1102,9 +1253,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn9() { await TestInMethodAsync( -@"int x; + """ + int x; -x = $$-x;", + x = $$-x; + """, MainDescription("int int.operator -(int value)")); } @@ -1112,9 +1265,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn10() { await TestInMethodAsync( -@"int x; + """ + int x; -x = unchecked ($$-x);", + x = unchecked ($$-x); + """, MainDescription("int int.operator -(int value)")); } @@ -1122,9 +1277,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn11() { await TestInMethodAsync( -@"int x; + """ + int x; -x = checked ($$-x);", + x = checked ($$-x); + """, MainDescription("int int.operator checked -(int value)")); } @@ -1132,9 +1289,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn12() { await TestInMethodAsync( -@"int x; + """ + int x; -x = x >>>$$ x;", + x = x >>>$$ x; + """, MainDescription("int int.operator >>>(int left, int right)")); } @@ -1142,9 +1301,11 @@ await TestInMethodAsync( public async Task TestOperatorBuiltIn13() { await TestInMethodAsync( -@"int x; + """ + int x; -x >>>=$$ x;", + x >>>=$$ x; + """, MainDescription("int int.operator >>>(int left, int right)")); } @@ -1152,10 +1313,12 @@ await TestInMethodAsync( public async Task TestOperatorCustomTypeBuiltIn_01() { var markup = -@"class C -{ - static void M() { C c; c = c +$$ c; } -}"; + """ + class C + { + static void M() { C c; c = c +$$ c; } + } + """; await TestAsync(markup); } @@ -1164,10 +1327,12 @@ public async Task TestOperatorCustomTypeBuiltIn_01() public async Task TestOperatorCustomTypeBuiltIn_02() { var markup = -@"class C -{ - static void M() { C c; c = c >>>$$ c; } -}"; + """ + class C + { + static void M() { C c; c = c >>>$$ c; } + } + """; await TestAsync(markup); } @@ -1176,11 +1341,13 @@ public async Task TestOperatorCustomTypeBuiltIn_02() public async Task TestOperatorCustomTypeOverload_01() { var markup = -@"class C -{ - static void M() { C c; c = c +$$ c; } - static C operator+(C a, C b) { return a; } -}"; + """ + class C + { + static void M() { C c; c = c +$$ c; } + static C operator+(C a, C b) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator +(C a, C b)")); @@ -1190,11 +1357,13 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_02() { var markup = -@"class C -{ - static void M() { C c; c = unchecked (c +$$ c); } - static C operator+(C a, C b) { return a; } -}"; + """ + class C + { + static void M() { C c; c = unchecked (c +$$ c); } + static C operator+(C a, C b) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator +(C a, C b)")); @@ -1204,12 +1373,14 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_03() { var markup = -@"class C -{ - static void M() { C c; c = unchecked (c +$$ c); } - static C operator+(C a, C b) { return a; } - static C operator checked +(C a, C b) { return a; } -}"; + """ + class C + { + static void M() { C c; c = unchecked (c +$$ c); } + static C operator+(C a, C b) { return a; } + static C operator checked +(C a, C b) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator +(C a, C b)")); @@ -1219,12 +1390,14 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_04() { var markup = -@"class C -{ - static void M() { C c; c = checked (c +$$ c); } - static C operator+(C a, C b) { return a; } - static C operator checked +(C a, C b) { return a; } -}"; + """ + class C + { + static void M() { C c; c = checked (c +$$ c); } + static C operator+(C a, C b) { return a; } + static C operator checked +(C a, C b) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator checked +(C a, C b)")); @@ -1234,11 +1407,13 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_05() { var markup = -@"class C -{ - static void M() { C c; c = $$-c; } - static C operator-(C a) { return a; } -}"; + """ + class C + { + static void M() { C c; c = $$-c; } + static C operator-(C a) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator -(C a)")); @@ -1248,11 +1423,13 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_06() { var markup = -@"class C -{ - static void M() { C c; c = unchecked ($$-c); } - static C operator-(C a) { return a; } -}"; + """ + class C + { + static void M() { C c; c = unchecked ($$-c); } + static C operator-(C a) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator -(C a)")); @@ -1262,12 +1439,14 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_07() { var markup = -@"class C -{ - static void M() { C c; c = unchecked ($$-c); } - static C operator-(C a) { return a; } - static C operator checked -(C a) { return a; } -}"; + """ + class C + { + static void M() { C c; c = unchecked ($$-c); } + static C operator-(C a) { return a; } + static C operator checked -(C a) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator -(C a)")); @@ -1277,12 +1456,14 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_08() { var markup = -@"class C -{ - static void M() { C c; c = checked ($$-c); } - static C operator-(C a) { return a; } - static C operator checked -(C a) { return a; } -}"; + """ + class C + { + static void M() { C c; c = checked ($$-c); } + static C operator-(C a) { return a; } + static C operator checked -(C a) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator checked -(C a)")); @@ -1292,11 +1473,13 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_09() { var markup = -@"class C -{ - static void M() { C c; c = c >>>$$ c; } - static C operator>>>(C a, C b) { return a; } -}"; + """ + class C + { + static void M() { C c; c = c >>>$$ c; } + static C operator>>>(C a, C b) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator >>>(C a, C b)")); @@ -1306,11 +1489,13 @@ await TestAsync(markup, public async Task TestOperatorCustomTypeOverload_10() { var markup = -@"class C -{ - static void M() { C c; c >>>=$$ c; } - static C operator>>>(C a, C b) { return a; } -}"; + """ + class C + { + static void M() { C c; c >>>=$$ c; } + static C operator>>>(C a, C b) { return a; } + } + """; await TestAsync(markup, MainDescription("C C.operator >>>(C a, C b)")); @@ -1320,12 +1505,14 @@ await TestAsync(markup, public async Task TestFieldInMethodMinimal() { var markup = -@"DateTime field; + """ + DateTime field; -void M() -{ - field$$ -}"; + void M() + { + field$$ + } + """; await TestInClassAsync(markup, MainDescription($"({FeaturesResources.field}) DateTime C.field")); @@ -1335,12 +1522,14 @@ await TestInClassAsync(markup, public async Task TestFieldInMethodQualified() { var markup = -@"System.IO.FileInfo file; + """ + System.IO.FileInfo file; -void M() -{ - file$$ -}"; + void M() + { + file$$ + } + """; await TestInClassAsync(markup, MainDescription($"({FeaturesResources.field}) System.IO.FileInfo C.file")); @@ -1350,9 +1539,11 @@ await TestInClassAsync(markup, public async Task TestMemberOfStructFromSource() { var markup = -@"struct MyStruct { -public static int SomeField; } -static class Test { int a = MyStruct.Some$$Field; }"; + """ + struct MyStruct { + public static int SomeField; } + static class Test { int a = MyStruct.Some$$Field; } + """; await TestAsync(markup, MainDescription($"({FeaturesResources.field}) static int MyStruct.SomeField")); @@ -1362,10 +1553,12 @@ await TestAsync(markup, public async Task TestMemberOfStructFromSourceWithDocComment() { var markup = -@"struct MyStruct { -///My Field -public static int SomeField; } -static class Test { int a = MyStruct.Some$$Field; }"; + """ + struct MyStruct { + ///My Field + public static int SomeField; } + static class Test { int a = MyStruct.Some$$Field; } + """; await TestAsync(markup, MainDescription($"({FeaturesResources.field}) static int MyStruct.SomeField"), @@ -1376,9 +1569,11 @@ await TestAsync(markup, public async Task TestMemberOfStructInsideMethodFromSource() { var markup = -@"struct MyStruct { -public static int SomeField; } -static class Test { static void Method() { int a = MyStruct.Some$$Field; } }"; + """ + struct MyStruct { + public static int SomeField; } + static class Test { static void Method() { int a = MyStruct.Some$$Field; } } + """; await TestAsync(markup, MainDescription($"({FeaturesResources.field}) static int MyStruct.SomeField")); @@ -1388,10 +1583,12 @@ await TestAsync(markup, public async Task TestMemberOfStructInsideMethodFromSourceWithDocComment() { var markup = -@"struct MyStruct { -///My Field -public static int SomeField; } -static class Test { static void Method() { int a = MyStruct.Some$$Field; } }"; + """ + struct MyStruct { + ///My Field + public static int SomeField; } + static class Test { static void Method() { int a = MyStruct.Some$$Field; } } + """; await TestAsync(markup, MainDescription($"({FeaturesResources.field}) static int MyStruct.SomeField"), @@ -1402,17 +1599,19 @@ await TestAsync(markup, public async Task TestPartialMethodDocComment_01() { var markup = -@"partial class MyClass -{ - ///My Method Definition - public partial void MyMethod(); + """ + partial class MyClass + { + ///My Method Definition + public partial void MyMethod(); - ///My Method Implementation - public partial void MyMethod() - { - } -} -static class Test { static void Method() { MyClass.My$$Method(); } }"; + ///My Method Implementation + public partial void MyMethod() + { + } + } + static class Test { static void Method() { MyClass.My$$Method(); } } + """; await TestAsync(markup, MainDescription($"void MyClass.MyMethod()"), @@ -1423,16 +1622,18 @@ await TestAsync(markup, public async Task TestPartialMethodDocComment_02() { var markup = -@"partial class MyClass -{ - ///My Method Definition - public partial void MyMethod(); + """ + partial class MyClass + { + ///My Method Definition + public partial void MyMethod(); - public partial void MyMethod() - { - } -} -static class Test { static void Method() { MyClass.My$$Method(); } }"; + public partial void MyMethod() + { + } + } + static class Test { static void Method() { MyClass.My$$Method(); } } + """; await TestAsync(markup, MainDescription($"void MyClass.MyMethod()"), @@ -1443,16 +1644,18 @@ await TestAsync(markup, public async Task TestPartialMethodDocComment_03() { var markup = -@"partial class MyClass -{ - public partial void MyMethod(); + """ + partial class MyClass + { + public partial void MyMethod(); - ///My Method Implementation - public partial void MyMethod() - { - } -} -static class Test { static void Method() { MyClass.My$$Method(); } }"; + ///My Method Implementation + public partial void MyMethod() + { + } + } + static class Test { static void Method() { MyClass.My$$Method(); } } + """; await TestAsync(markup, MainDescription($"void MyClass.MyMethod()"), @@ -1463,12 +1666,14 @@ await TestAsync(markup, public async Task TestPartialMethodDocComment_04() { var markup = -@"partial class MyClass -{ - ///My Method Definition - public partial void MyMethod(); -} -static class Test { static void Method() { MyClass.My$$Method(); } }"; + """ + partial class MyClass + { + ///My Method Definition + public partial void MyMethod(); + } + static class Test { static void Method() { MyClass.My$$Method(); } } + """; await TestAsync(markup, MainDescription($"void MyClass.MyMethod()"), @@ -1479,12 +1684,14 @@ await TestAsync(markup, public async Task TestPartialMethodDocComment_05() { var markup = -@"partial class MyClass -{ - ///My Method Implementation - public partial void MyMethod() { } -} -static class Test { static void Method() { MyClass.My$$Method(); } }"; + """ + partial class MyClass + { + ///My Method Implementation + public partial void MyMethod() { } + } + static class Test { static void Method() { MyClass.My$$Method(); } } + """; await TestAsync(markup, MainDescription($"void MyClass.MyMethod()"), @@ -1495,14 +1702,16 @@ await TestAsync(markup, public async Task TestPartialMethodDocComment_06() { var markup = -@"partial class MyClass -{ - ///My Method Definition - partial void MyMethod(); + """ + partial class MyClass + { + ///My Method Definition + partial void MyMethod(); - partial void MyMethod() { } -} -static class Test { static void Method() { MyClass.My$$Method(); } }"; + partial void MyMethod() { } + } + static class Test { static void Method() { MyClass.My$$Method(); } } + """; await TestAsync(markup, MainDescription($"void MyClass.MyMethod()"), @@ -1521,12 +1730,14 @@ public async Task TestMetadataFieldQualified1() { // NOTE: we qualify the field type, but not the type that contains the field in Dev10 var markup = -@"class C { - void M() - { - DateTime dt = System.DateTime.MaxValue$$ - } -}"; + """ + class C { + void M() + { + DateTime dt = System.DateTime.MaxValue$$ + } + } + """; await TestAsync(markup, MainDescription($"({FeaturesResources.field}) static readonly System.DateTime System.DateTime.MaxValue")); } @@ -1535,13 +1746,15 @@ await TestAsync(markup, public async Task TestMetadataFieldQualified2() { await TestAsync( -@"class C -{ - void M() - { - DateTime dt = System.DateTime.MaxValue$$ - } -}", + """ + class C + { + void M() + { + DateTime dt = System.DateTime.MaxValue$$ + } + } + """, MainDescription($"({FeaturesResources.field}) static readonly System.DateTime System.DateTime.MaxValue")); } @@ -1549,15 +1762,17 @@ void M() public async Task TestMetadataFieldQualified3() { await TestAsync( -@"using System; + """ + using System; -class C -{ - void M() - { - DateTime dt = System.DateTime.MaxValue$$ - } -}", + class C + { + void M() + { + DateTime dt = System.DateTime.MaxValue$$ + } + } + """, MainDescription($"({FeaturesResources.field}) static readonly DateTime DateTime.MaxValue")); } @@ -1565,34 +1780,38 @@ void M() public async Task ConstructedGenericField() { await TestAsync( -@"class C -{ - public T Field; -} + """ + class C + { + public T Field; + } -class D -{ - void M() - { - new C().Fi$$eld.ToString(); - } -}", - MainDescription($"({FeaturesResources.field}) int C.Field")); + class D + { + void M() + { + new C().Fi$$eld.ToString(); + } + } + """, + MainDescription($"({FeaturesResources.field}) int C.Field")); } [Fact] public async Task UnconstructedGenericField() { await TestAsync( -@"class C -{ - public T Field; + """ + class C + { + public T Field; - void M() - { - Fi$$eld.ToString(); - } -}", + void M() + { + Fi$$eld.ToString(); + } + } + """, MainDescription($"({FeaturesResources.field}) T C.Field")); } @@ -1638,185 +1857,209 @@ await TestInMethodAsync(@"string f = default$$", [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756226")] public async Task TestAwaitKeywordOnGenericTaskReturningAsync() { - var markup = @"using System.Threading.Tasks; -class C -{ - public async Task Calc() - { - aw$$ait Calc(); - return 5; - } -}"; + var markup = """ + using System.Threading.Tasks; + class C + { + public async Task Calc() + { + aw$$ait Calc(); + return 5; + } + } + """; await TestAsync(markup, MainDescription(string.Format(FeaturesResources.Awaited_task_returns_0, "struct System.Int32"))); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756226")] public async Task TestAwaitKeywordInDeclarationStatement() { - var markup = @"using System.Threading.Tasks; -class C -{ - public async Task Calc() - { - var x = $$await Calc(); - return 5; - } -}"; + var markup = """ + using System.Threading.Tasks; + class C + { + public async Task Calc() + { + var x = $$await Calc(); + return 5; + } + } + """; await TestAsync(markup, MainDescription(string.Format(FeaturesResources.Awaited_task_returns_0, "struct System.Int32"))); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756226")] public async Task TestAwaitKeywordOnTaskReturningAsync() { - var markup = @"using System.Threading.Tasks; -class C -{ - public async void Calc() - { - aw$$ait Task.Delay(100); - } -}"; + var markup = """ + using System.Threading.Tasks; + class C + { + public async void Calc() + { + aw$$ait Task.Delay(100); + } + } + """; await TestAsync(markup, MainDescription(FeaturesResources.Awaited_task_returns_no_value)); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756226"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756337")] public async Task TestNestedAwaitKeywords1() { - var markup = @"using System; -using System.Threading.Tasks; -class AsyncExample2 -{ - async Task> AsyncMethod() - { - return NewMethod(); - } + var markup = """ + using System; + using System.Threading.Tasks; + class AsyncExample2 + { + async Task> AsyncMethod() + { + return NewMethod(); + } - private static Task NewMethod() - { - int hours = 24; - return hours; - } + private static Task NewMethod() + { + int hours = 24; + return hours; + } - async Task UseAsync() - { - Func> lambda = async () => - { - return await await AsyncMethod(); - }; + async Task UseAsync() + { + Func> lambda = async () => + { + return await await AsyncMethod(); + }; - int result = await await AsyncMethod(); - Task> resultTask = AsyncMethod(); - result = await awa$$it resultTask; - result = await lambda(); - } -}"; + int result = await await AsyncMethod(); + Task> resultTask = AsyncMethod(); + result = await awa$$it resultTask; + result = await lambda(); + } + } + """; await TestAsync(markup, MainDescription(string.Format(FeaturesResources.Awaited_task_returns_0, $"({CSharpFeaturesResources.awaitable}) class System.Threading.Tasks.Task")), - TypeParameterMap($"\r\nTResult {FeaturesResources.is_} int")); + TypeParameterMap($""" + + TResult {FeaturesResources.is_} int + """)); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756226")] public async Task TestNestedAwaitKeywords2() { - var markup = @"using System; -using System.Threading.Tasks; -class AsyncExample2 -{ - async Task> AsyncMethod() - { - return NewMethod(); - } + var markup = """ + using System; + using System.Threading.Tasks; + class AsyncExample2 + { + async Task> AsyncMethod() + { + return NewMethod(); + } - private static Task NewMethod() - { - int hours = 24; - return hours; - } + private static Task NewMethod() + { + int hours = 24; + return hours; + } - async Task UseAsync() - { - Func> lambda = async () => - { - return await await AsyncMethod(); - }; + async Task UseAsync() + { + Func> lambda = async () => + { + return await await AsyncMethod(); + }; - int result = await await AsyncMethod(); - Task> resultTask = AsyncMethod(); - result = awa$$it await resultTask; - result = await lambda(); - } -}"; + int result = await await AsyncMethod(); + Task> resultTask = AsyncMethod(); + result = awa$$it await resultTask; + result = await lambda(); + } + } + """; await TestAsync(markup, MainDescription(string.Format(FeaturesResources.Awaited_task_returns_0, "struct System.Int32"))); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756226"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756337")] public async Task TestAwaitablePrefixOnCustomAwaiter() { - var markup = @"using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using Z = $$C; + var markup = """ + using System; + using System.Runtime.CompilerServices; + using System.Threading.Tasks; + using Z = $$C; -class C -{ - public MyAwaiter GetAwaiter() { throw new NotImplementedException(); } -} + class C + { + public MyAwaiter GetAwaiter() { throw new NotImplementedException(); } + } -class MyAwaiter : INotifyCompletion -{ - public void OnCompleted(Action continuation) - { - throw new NotImplementedException(); - } + class MyAwaiter : INotifyCompletion + { + public void OnCompleted(Action continuation) + { + throw new NotImplementedException(); + } - public bool IsCompleted { get { throw new NotImplementedException(); } } - public void GetResult() { } -}"; + public bool IsCompleted { get { throw new NotImplementedException(); } } + public void GetResult() { } + } + """; await TestAsync(markup, MainDescription($"({CSharpFeaturesResources.awaitable}) class C")); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756226"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756337")] public async Task TestTaskType() { - var markup = @"using System.Threading.Tasks; -class C -{ - public void Calc() - { - Task$$ v1; - } -}"; + var markup = """ + using System.Threading.Tasks; + class C + { + public void Calc() + { + Task$$ v1; + } + } + """; await TestAsync(markup, MainDescription($"({CSharpFeaturesResources.awaitable}) class System.Threading.Tasks.Task")); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756226"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/756337")] public async Task TestTaskOfTType() { - var markup = @"using System; -using System.Threading.Tasks; -class C -{ - public void Calc() - { - Task$$ v1; - } -}"; + var markup = """ + using System; + using System.Threading.Tasks; + class C + { + public void Calc() + { + Task$$ v1; + } + } + """; await TestAsync(markup, MainDescription($"({CSharpFeaturesResources.awaitable}) class System.Threading.Tasks.Task"), - TypeParameterMap($"\r\nTResult {FeaturesResources.is_} int")); + TypeParameterMap($""" + + TResult {FeaturesResources.is_} int + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7100")] public async Task TestDynamicIsntAwaitable() { - var markup = @" -class C -{ - dynamic D() { return null; } - void M() - { - D$$(); - } -} -"; + var markup = """ + + class C + { + dynamic D() { return null; } + void M() + { + D$$(); + } + } + + """; await TestAsync(markup, MainDescription("dynamic C.D()")); } @@ -1833,7 +2076,10 @@ public async Task TestStringLiteralUtf8_01() await TestInMethodAsync(@"var f = ""Goo""u8$$", TestSources.Span, MainDescription("readonly ref struct System.ReadOnlySpan"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} byte")); + TypeParameterMap($""" + + T {FeaturesResources.is_} byte + """)); } [Fact] @@ -1842,7 +2088,10 @@ public async Task TestStringLiteralUtf8_02() await TestInMethodAsync(@"var f = ""Goo""U8$$", TestSources.Span, MainDescription("readonly ref struct System.ReadOnlySpan"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} byte")); + TypeParameterMap($""" + + T {FeaturesResources.is_} byte + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1280")] @@ -1858,7 +2107,10 @@ public async Task TestVerbatimStringLiteralUtf8_01() await TestInMethodAsync(@"string f = @""cat""u8$$", TestSources.Span, MainDescription("readonly ref struct System.ReadOnlySpan"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} byte")); + TypeParameterMap($""" + + T {FeaturesResources.is_} byte + """)); } [Fact] @@ -1867,7 +2119,10 @@ public async Task TestVerbatimStringLiteralUtf8_02() await TestInMethodAsync(@"string f = @""cat""U8$$", TestSources.Span, MainDescription("readonly ref struct System.ReadOnlySpan"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} byte")); + TypeParameterMap($""" + + T {FeaturesResources.is_} byte + """)); } [Fact] @@ -1883,7 +2138,10 @@ public async Task TestRawStringLiteralUtf8_01() await TestInMethodAsync(@"string f = """"""Goo""""""u8$$", TestSources.Span, MainDescription("readonly ref struct System.ReadOnlySpan"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} byte")); + TypeParameterMap($""" + + T {FeaturesResources.is_} byte + """)); } [Fact] @@ -1892,56 +2150,83 @@ public async Task TestRawStringLiteralUtf8_02() await TestInMethodAsync(@"string f = """"""Goo""""""U8$$", TestSources.Span, MainDescription("readonly ref struct System.ReadOnlySpan"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} byte")); + TypeParameterMap($""" + + T {FeaturesResources.is_} byte + """)); } [Fact] public async Task TestRawStringLiteralMultiline() { - await TestInMethodAsync(@"string f = """""" - Goo - """"""$$", + await TestInMethodAsync("""" + string f = """ + Goo + """$$ + """", MainDescription("class System.String")); } [Fact] public async Task TestRawStringLiteralMultilineUtf8_01() { - await TestInMethodAsync(@"string f = """""" - Goo - """"""u8$$", + await TestInMethodAsync("""" + string f = """ + Goo + """u8$$ + """", TestSources.Span, MainDescription("readonly ref struct System.ReadOnlySpan"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} byte")); + TypeParameterMap($""" + + T {FeaturesResources.is_} byte + """)); } [Fact] public async Task TestRawStringLiteralMultilineUtf8_02() { - await TestInMethodAsync(@"string f = """""" - Goo - """"""U8$$", + await TestInMethodAsync("""" + string f = """ + Goo + """U8$$ + """", TestSources.Span, MainDescription("readonly ref struct System.ReadOnlySpan"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} byte")); + TypeParameterMap($""" + + T {FeaturesResources.is_} byte + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1280")] public async Task TestInterpolatedStringLiteral() { await TestInMethodAsync(@"string f = $""cat""$$", MainDescription("class System.String")); - await TestInMethodAsync(@"string f = $""c$$at""", MainDescription("class System.String")); - await TestInMethodAsync(@"string f = $""$$cat""", MainDescription("class System.String")); - await TestInMethodAsync(@"string f = $""cat {1$$ + 2} dog""", MainDescription("struct System.Int32")); + await TestInMethodAsync(""" + string f = $"c$$at" + """, MainDescription("class System.String")); + await TestInMethodAsync(""" + string f = $"$$cat" + """, MainDescription("class System.String")); + await TestInMethodAsync(""" + string f = $"cat {1$$ + 2} dog" + """, MainDescription("struct System.Int32")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1280")] public async Task TestVerbatimInterpolatedStringLiteral() { await TestInMethodAsync(@"string f = $@""cat""$$", MainDescription("class System.String")); - await TestInMethodAsync(@"string f = $@""c$$at""", MainDescription("class System.String")); - await TestInMethodAsync(@"string f = $@""$$cat""", MainDescription("class System.String")); - await TestInMethodAsync(@"string f = $@""cat {1$$ + 2} dog""", MainDescription("struct System.Int32")); + await TestInMethodAsync(""" + string f = $@"c$$at" + """, MainDescription("class System.String")); + await TestInMethodAsync(""" + string f = $@"$$cat" + """, MainDescription("class System.String")); + await TestInMethodAsync(""" + string f = $@"cat {1$$ + 2} dog" + """, MainDescription("struct System.Int32")); } [Fact] @@ -1955,7 +2240,7 @@ await TestInMethodAsync(@"string f = 'x'$$", public async Task DynamicKeyword() { await TestInMethodAsync( -@"dyn$$amic dyn;", + @"dyn$$amic dyn;", MainDescription("dynamic"), Documentation(FeaturesResources.Represents_an_object_whose_operations_will_be_resolved_at_runtime)); } @@ -1964,12 +2249,14 @@ await TestInMethodAsync( public async Task DynamicField() { await TestInClassAsync( -@"dynamic dyn; + """ + dynamic dyn; -void M() -{ - d$$yn.Goo(); -}", + void M() + { + d$$yn.Goo(); + } + """, MainDescription($"({FeaturesResources.field}) dynamic C.dyn")); } @@ -1977,12 +2264,14 @@ void M() public async Task LocalProperty_Minimal() { await TestInClassAsync( -@"DateTime Prop { get; set; } + """ + DateTime Prop { get; set; } -void M() -{ - P$$rop.ToString(); -}", + void M() + { + P$$rop.ToString(); + } + """, MainDescription("DateTime C.Prop { get; set; }")); } @@ -1990,12 +2279,14 @@ void M() public async Task LocalProperty_Minimal_PrivateSet() { await TestInClassAsync( -@"public DateTime Prop { get; private set; } + """ + public DateTime Prop { get; private set; } -void M() -{ - P$$rop.ToString(); -}", + void M() + { + P$$rop.ToString(); + } + """, MainDescription("DateTime C.Prop { get; private set; }")); } @@ -2003,12 +2294,14 @@ void M() public async Task LocalProperty_Minimal_PrivateSet1() { await TestInClassAsync( -@"protected internal int Prop { get; private set; } + """ + protected internal int Prop { get; private set; } -void M() -{ - P$$rop.ToString(); -}", + void M() + { + P$$rop.ToString(); + } + """, MainDescription("int C.Prop { get; private set; }")); } @@ -2016,12 +2309,14 @@ void M() public async Task LocalProperty_Qualified() { await TestInClassAsync( -@"System.IO.FileInfo Prop { get; set; } + """ + System.IO.FileInfo Prop { get; set; } -void M() -{ - P$$rop.ToString(); -}", + void M() + { + P$$rop.ToString(); + } + """, MainDescription("System.IO.FileInfo C.Prop { get; set; }")); } @@ -2036,9 +2331,11 @@ public async Task NonLocalProperty_Minimal() public async Task NonLocalProperty_Qualified() { await TestInMethodAsync( -@"System.IO.FileInfo f; + """ + System.IO.FileInfo f; -f.Att$$ributes.ToString();", + f.Att$$ributes.ToString(); + """, MainDescription("System.IO.FileAttributes System.IO.FileSystemInfo.Attributes { get; set; }")); } @@ -2046,18 +2343,20 @@ await TestInMethodAsync( public async Task ConstructedGenericProperty() { await TestAsync( -@"class C -{ - public T Property { get; set } -} + """ + class C + { + public T Property { get; set } + } -class D -{ - void M() - { - new C().Pro$$perty.ToString(); - } -}", + class D + { + void M() + { + new C().Pro$$perty.ToString(); + } + } + """, MainDescription("int C.Property { get; set; }")); } @@ -2065,15 +2364,17 @@ void M() public async Task UnconstructedGenericProperty() { await TestAsync( -@"class C -{ - public T Property { get; set} + """ + class C + { + public T Property { get; set} - void M() - { - Pro$$perty.ToString(); - } -}", + void M() + { + Pro$$perty.ToString(); + } + } + """, MainDescription("T C.Property { get; set; }")); } @@ -2081,13 +2382,15 @@ void M() public async Task ValueInProperty() { await TestInClassAsync( -@"public DateTime Property -{ - set - { - goo = val$$ue; - } -}", + """ + public DateTime Property + { + set + { + goo = val$$ue; + } + } + """, MainDescription($"({FeaturesResources.parameter}) DateTime value")); } @@ -2108,100 +2411,116 @@ await TestInClassAsync(@"enum E$$ : byte { A, B }", [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] public async Task EnumNonDefaultUnderlyingType_AsField() { - await TestInClassAsync(@" -enum E : byte { A, B } + await TestInClassAsync(""" -private E$$ _E; -", + enum E : byte { A, B } + + private E$$ _E; + + """, MainDescription("enum C.E : byte")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] public async Task EnumNonDefaultUnderlyingType_AsProperty() { - await TestInClassAsync(@" -enum E : byte { A, B } + await TestInClassAsync(""" + + enum E : byte { A, B } -private E$$ E{ get; set; }; -", + private E$$ E{ get; set; }; + + """, MainDescription("enum C.E : byte")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] public async Task EnumNonDefaultUnderlyingType_AsParameter() { - await TestInClassAsync(@" -enum E : byte { A, B } + await TestInClassAsync(""" + + enum E : byte { A, B } + + private void M(E$$ e) { } -private void M(E$$ e) { } -", + """, MainDescription("enum C.E : byte")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] public async Task EnumNonDefaultUnderlyingType_AsReturnType() { - await TestInClassAsync(@" -enum E : byte { A, B } + await TestInClassAsync(""" + + enum E : byte { A, B } -private E$$ M() { } -", + private E$$ M() { } + + """, MainDescription("enum C.E : byte")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] public async Task EnumNonDefaultUnderlyingType_AsLocal() { - await TestInClassAsync(@" -enum E : byte { A, B } + await TestInClassAsync(""" -private void M() -{ - E$$ e = default; -} -", + enum E : byte { A, B } + + private void M() + { + E$$ e = default; + } + + """, MainDescription("enum C.E : byte")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] public async Task EnumNonDefaultUnderlyingType_OnMemberAccessOnType() { - await TestInClassAsync(@" -enum EN : byte { A, B } + await TestInClassAsync(""" -private void M() -{ - var ea = E$$N.A; -} -", + enum EN : byte { A, B } + + private void M() + { + var ea = E$$N.A; + } + + """, MainDescription("enum C.EN : byte")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] public async Task EnumNonDefaultUnderlyingType_OnMemberAccessOnType_OnDot() { - await TestInClassAsync(@" -enum E : byte { A, B } + await TestInClassAsync(""" -private void M() -{ - var ea = E$$.A; -} -", - MainDescription("E.A = 0")); - } + enum E : byte { A, B } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] - public async Task EnumNonDefaultUnderlyingType_NotOnMemberAccessOnMember() + private void M() + { + var ea = E$$.A; + } + + """, + MainDescription("E.A = 0")); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52490")] + public async Task EnumNonDefaultUnderlyingType_NotOnMemberAccessOnMember() { - await TestInClassAsync(@" -enum E : byte { A, B } + await TestInClassAsync(""" -private void M() -{ - var ea = E.A$$; -} -", + enum E : byte { A, B } + + private void M() + { + var ea = E.A$$; + } + + """, MainDescription("E.A = 0")); } @@ -2223,11 +2542,13 @@ private void M() [InlineData("ulong", "System.UInt64")] public async Task EnumNonDefaultUnderlyingType_ShowForNonDefaultTypes(string displayTypeName, string underlyingTypeName) { - await TestInClassAsync(@$" -enum E$$ : {underlyingTypeName} -{{ - A, B -}}", + await TestInClassAsync($$""" + + enum E$$ : {{underlyingTypeName}} + { + A, B + } + """, MainDescription($"enum C.E : {displayTypeName}")); } @@ -2237,11 +2558,13 @@ enum E$$ : {underlyingTypeName} [InlineData(": System.Int32")] public async Task EnumNonDefaultUnderlyingType_DoNotShowForDefaultType(string defaultType) { - await TestInClassAsync(@$" -enum E$$ {defaultType} -{{ - A, B -}}", + await TestInClassAsync($$""" + + enum E$$ {{defaultType}} + { + A, B + } + """, MainDescription("enum C.E")); } @@ -2270,20 +2593,22 @@ await TestInMethodAsync(@"AttributeTargets a = AttributeTargets.A$$ll", public async Task EnumMemberNameFromSource1() { await TestAsync( -@"enum E -{ - A = 1 << 0, - B = 1 << 1, - C = 1 << 2 -} + """ + enum E + { + A = 1 << 0, + B = 1 << 1, + C = 1 << 2 + } -class C -{ - void M() - { - var e = E.B$$; - } -}", + class C + { + void M() + { + var e = E.B$$; + } + } + """, MainDescription("E.B = 1 << 1")); } @@ -2291,20 +2616,22 @@ void M() public async Task EnumMemberNameFromSource2() { await TestAsync( -@"enum E -{ - A, - B, - C -} + """ + enum E + { + A, + B, + C + } -class C -{ - void M() - { - var e = E.B$$; - } -}", + class C + { + void M() + { + var e = E.B$$; + } + } + """, MainDescription("E.B = 1")); } @@ -2312,9 +2639,11 @@ void M() public async Task Parameter_InMethod_Minimal() { await TestInClassAsync( -@"void M(DateTime dt) -{ - d$$t.ToString();", + """ + void M(DateTime dt) + { + d$$t.ToString(); + """, MainDescription($"({FeaturesResources.parameter}) DateTime dt")); } @@ -2322,9 +2651,11 @@ await TestInClassAsync( public async Task Parameter_InMethod_Qualified() { await TestInClassAsync( -@"void M(System.IO.FileInfo fileInfo) -{ - file$$Info.ToString();", + """ + void M(System.IO.FileInfo fileInfo) + { + file$$Info.ToString(); + """, MainDescription($"({FeaturesResources.parameter}) System.IO.FileInfo fileInfo")); } @@ -2341,10 +2672,12 @@ public async Task Parameter_DefaultValue() // NOTE: Dev10 doesn't show the default value, but it would be nice if we did. // NOTE: The "DefaultValue" property isn't implemented yet. await TestInClassAsync( -@"void M(int param = 42) -{ - para$$m.ToString(); -}", + """ + void M(int param = 42) + { + para$$m.ToString(); + } + """, MainDescription($"({FeaturesResources.parameter}) int param = 42")); } @@ -2352,9 +2685,11 @@ await TestInClassAsync( public async Task Lambda_Parameter_DefaultValue_01() { await TestInMethodAsync( -@"(int param = 42) => { - return para$$m + 1; -}", + """ + (int param = 42) => { + return para$$m + 1; + } + """, MainDescription($"({FeaturesResources.parameter}) int param = 42")); } @@ -2362,9 +2697,11 @@ await TestInMethodAsync( public async Task Lambda_Parameter_DefaultValue_02() { await TestInMethodAsync( -@"(int param = $$int.MaxValue) => { - return param + 1; -}", + """ + (int param = $$int.MaxValue) => { + return param + 1; + } + """, MainDescription($"{FeaturesResources.struct_} System.Int32")); } @@ -2372,9 +2709,11 @@ await TestInMethodAsync( public async Task Lambda_Parameter_DefaultValue_03() { await TestInMethodAsync( -@"(int param = int.$$MaxValue) => { - return param + 1; -}", + """ + (int param = int.$$MaxValue) => { + return param + 1; + } + """, MainDescription($"({FeaturesResources.constant}) const int int.MaxValue = 2147483647")); } @@ -2382,9 +2721,11 @@ await TestInMethodAsync( public async Task Lambda_Parameter_ParamsArray() { await TestInMethodAsync( -@"(params int[] xs) => { - return x$$s.Length; -}", + """ + (params int[] xs) => { + return x$$s.Length; + } + """, MainDescription($"({FeaturesResources.parameter}) params int[] xs")); } @@ -2392,10 +2733,12 @@ await TestInMethodAsync( public async Task Parameter_Params() { await TestInClassAsync( -@"void M(params DateTime[] arg) -{ - ar$$g.ToString(); -}", + """ + void M(params DateTime[] arg) + { + ar$$g.ToString(); + } + """, MainDescription($"({FeaturesResources.parameter}) params DateTime[] arg")); } @@ -2403,10 +2746,12 @@ await TestInClassAsync( public async Task Parameter_Ref() { await TestInClassAsync( -@"void M(ref DateTime arg) -{ - ar$$g.ToString(); -}", + """ + void M(ref DateTime arg) + { + ar$$g.ToString(); + } + """, MainDescription($"({FeaturesResources.parameter}) ref DateTime arg")); } @@ -2414,10 +2759,12 @@ await TestInClassAsync( public async Task Parameter_Out() { await TestInClassAsync( -@"void M(out DateTime arg) -{ - ar$$g.ToString(); -}", + """ + void M(out DateTime arg) + { + ar$$g.ToString(); + } + """, MainDescription($"({FeaturesResources.parameter}) out DateTime arg")); } @@ -2425,9 +2772,11 @@ await TestInClassAsync( public async Task Local_Minimal() { await TestInMethodAsync( -@"DateTime dt; + """ + DateTime dt; -d$$t.ToString();", + d$$t.ToString(); + """, MainDescription($"({FeaturesResources.local_variable}) DateTime dt")); } @@ -2435,9 +2784,11 @@ await TestInMethodAsync( public async Task Local_Qualified() { await TestInMethodAsync( -@"System.IO.FileInfo fileInfo; + """ + System.IO.FileInfo fileInfo; -file$$Info.ToString();", + file$$Info.ToString(); + """, MainDescription($"({FeaturesResources.local_variable}) System.IO.FileInfo fileInfo")); } @@ -2452,14 +2803,16 @@ public async Task Method_MetadataOverload() public async Task Method_SimpleWithOverload() { await TestInClassAsync( -@"void Method() -{ - Met$$hod(); -} + """ + void Method() + { + Met$$hod(); + } -void Method(int i) -{ -}", + void Method(int i) + { + } + """, MainDescription($"void C.Method() (+ 1 {FeaturesResources.overload})")); } @@ -2467,22 +2820,24 @@ void Method(int i) public async Task Method_MoreOverloads() { await TestInClassAsync( -@"void Method() -{ - Met$$hod(null); -} + """ + void Method() + { + Met$$hod(null); + } -void Method(int i) -{ -} + void Method(int i) + { + } -void Method(DateTime dt) -{ -} + void Method(DateTime dt) + { + } -void Method(System.IO.FileInfo fileInfo) -{ -}", + void Method(System.IO.FileInfo fileInfo) + { + } + """, MainDescription($"void C.Method(System.IO.FileInfo fileInfo) (+ 3 {FeaturesResources.overloads_})")); } @@ -2490,10 +2845,12 @@ void Method(System.IO.FileInfo fileInfo) public async Task Method_SimpleInSameClass() { await TestInClassAsync( -@"DateTime GetDate(System.IO.FileInfo ft) -{ - Get$$Date(null); -}", + """ + DateTime GetDate(System.IO.FileInfo ft) + { + Get$$Date(null); + } + """, MainDescription("DateTime C.GetDate(System.IO.FileInfo ft)")); } @@ -2501,14 +2858,16 @@ await TestInClassAsync( public async Task Method_OptionalParameter() { await TestInClassAsync( -@"void M() -{ - Met$$hod(); -} + """ + void M() + { + Met$$hod(); + } -void Method(int i = 0) -{ -}", + void Method(int i = 0) + { + } + """, MainDescription("void C.Method([int i = 0])")); } @@ -2516,9 +2875,11 @@ void Method(int i = 0) public async Task Method_OptionalDecimalParameter() { await TestInClassAsync( -@"void Goo(decimal x$$yz = 10) -{ -}", + """ + void Goo(decimal x$$yz = 10) + { + } + """, MainDescription($"({FeaturesResources.parameter}) decimal xyz = 10")); } @@ -2528,10 +2889,12 @@ public async Task Method_Generic() // Generic method don't get the instantiation info yet. NOTE: We don't display // constraint info in Dev10. Should we? await TestInClassAsync( -@"TOut Goo(TIn arg) where TIn : IEquatable -{ - Go$$o(37); -}", + """ + TOut Goo(TIn arg) where TIn : IEquatable + { + Go$$o(37); + } + """, MainDescription("DateTime C.Goo(int arg)")); } @@ -2540,10 +2903,12 @@ await TestInClassAsync( public async Task Method_UnconstructedGeneric() { await TestInClassAsync( -@"TOut Goo(TIn arg) -{ - Go$$o(default(TIn); -}", + """ + TOut Goo(TIn arg) + { + Go$$o(default(TIn); + } + """, MainDescription("TOut C.Goo(TIn arg)")); } @@ -2552,10 +2917,12 @@ await TestInClassAsync( public async Task Method_Inferred() { await TestInClassAsync( -@"void Goo(TIn arg) -{ - Go$$o(42); -}", + """ + void Goo(TIn arg) + { + Go$$o(42); + } + """, MainDescription("void C.Goo(int arg)")); } @@ -2563,10 +2930,12 @@ await TestInClassAsync( public async Task Method_MultipleParams() { await TestInClassAsync( -@"void Goo(DateTime dt, System.IO.FileInfo fi, int number) -{ - Go$$o(DateTime.Now, null, 32); -}", + """ + void Goo(DateTime dt, System.IO.FileInfo fi, int number) + { + Go$$o(DateTime.Now, null, 32); + } + """, MainDescription("void C.Goo(DateTime dt, System.IO.FileInfo fi, int number)")); } @@ -2575,10 +2944,12 @@ public async Task Method_OptionalParam() { // NOTE - Default values aren't actually returned by symbols yet. await TestInClassAsync( -@"void Goo(int num = 42) -{ - Go$$o(); -}", + """ + void Goo(int num = 42) + { + Go$$o(); + } + """, MainDescription("void C.Goo([int num = 42])")); } @@ -2587,10 +2958,12 @@ public async Task Method_ParameterModifiers() { // NOTE - Default values aren't actually returned by symbols yet. await TestInClassAsync( -@"void Goo(ref DateTime dt, out System.IO.FileInfo fi, params int[] numbers) -{ - Go$$o(DateTime.Now, null, 32); -}", + """ + void Goo(ref DateTime dt, out System.IO.FileInfo fi, params int[] numbers) + { + Go$$o(DateTime.Now, null, 32); + } + """, MainDescription("void C.Goo(ref DateTime dt, out System.IO.FileInfo fi, params int[] numbers)")); } @@ -2611,14 +2984,16 @@ void Goo(ref readonly DateTime dt, ref readonly System.IO.FileInfo fi, params in public async Task Constructor() { await TestInClassAsync( -@"public C() -{ -} + """ + public C() + { + } -void M() -{ - new C$$().ToString(); -}", + void M() + { + new C$$().ToString(); + } + """, MainDescription("C.C()")); } @@ -2626,22 +3001,24 @@ void M() public async Task Constructor_Overloads() { await TestInClassAsync( -@"public C() -{ -} + """ + public C() + { + } -public C(DateTime dt) -{ -} + public C(DateTime dt) + { + } -public C(int i) -{ -} + public C(int i) + { + } -void M() -{ - new C$$(DateTime.MaxValue).ToString(); -}", + void M() + { + new C$$(DateTime.MaxValue).ToString(); + } + """, MainDescription($"C.C(DateTime dt) (+ 2 {FeaturesResources.overloads_})")); } @@ -2652,7 +3029,7 @@ void M() public async Task Constructor_OverloadFromStringLiteral() { await TestInMethodAsync( -@"new InvalidOperatio$$nException("""");", + @"new InvalidOperatio$$nException("""");", MainDescription($"InvalidOperationException.InvalidOperationException(string message) (+ 2 {FeaturesResources.overloads_})")); } @@ -2663,10 +3040,12 @@ await TestInMethodAsync( public async Task Constructor_UnknownType() { await TestInvalidTypeInClassAsync( -@"void M() -{ - new G$$oo(); -}"); + """ + void M() + { + new G$$oo(); + } + """); } /// @@ -2676,7 +3055,7 @@ await TestInvalidTypeInClassAsync( public async Task Constructor_OverloadFromProperty() { await TestInMethodAsync( -@"new InvalidOperatio$$nException(this.GetType().Name);", + @"new InvalidOperatio$$nException(this.GetType().Name);", MainDescription($"InvalidOperationException.InvalidOperationException(string message) (+ 2 {FeaturesResources.overloads_})")); } @@ -2684,7 +3063,7 @@ await TestInMethodAsync( public async Task Constructor_Metadata() { await TestInMethodAsync( -@"new Argument$$NullException();", + @"new Argument$$NullException();", MainDescription($"ArgumentNullException.ArgumentNullException() (+ 3 {FeaturesResources.overloads_})")); } @@ -2699,10 +3078,12 @@ public async Task Constructor_MetadataQualified() public async Task InterfaceProperty() { await TestInMethodAsync( -@"interface I -{ - string Name$$ { get; set; } -}", + """ + interface I + { + string Name$$ { get; set; } + } + """, MainDescription("string I.Name { get; set; }")); } @@ -2710,25 +3091,27 @@ await TestInMethodAsync( public async Task ExplicitInterfacePropertyImplementation() { await TestInMethodAsync( -@"interface I -{ - string Name { get; set; } -} + """ + interface I + { + string Name { get; set; } + } -class C : I -{ - string IEmployee.Name$$ - { - get - { - return """"; - } + class C : I + { + string IEmployee.Name$$ + { + get + { + return ""; + } - set - { - } - } -}", + set + { + } + } + } + """, MainDescription("string C.Name { get; set; }")); } @@ -2736,15 +3119,17 @@ string IEmployee.Name$$ public async Task Operator() { await TestInClassAsync( -@"public static C operator +(C left, C right) -{ - return null; -} + """ + public static C operator +(C left, C right) + { + return null; + } -void M(C left, C right) -{ - return left +$$ right; -}", + void M(C left, C right) + { + return left +$$ right; + } + """, MainDescription("C C.operator +(C left, C right)")); } @@ -2755,9 +3140,11 @@ void M(C left, C right) public async Task GenericMethodWithConstraintsAtDeclaration() { await TestInClassAsync( -@"TOut G$$oo(TIn arg) where TIn : IEquatable -{ -}", + """ + TOut G$$oo(TIn arg) where TIn : IEquatable + { + } + """, MainDescription("TOut C.Goo(TIn arg) where TIn : IEquatable")); } @@ -2769,10 +3156,12 @@ await TestInClassAsync( public async Task GenericMethodWithMultipleConstraintsAtDeclaration() { await TestInClassAsync( -@"TOut Goo(TIn arg) where TIn : Employee, new() -{ - Go$$o(default(TIn); -}", + """ + TOut Goo(TIn arg) where TIn : Employee, new() + { + Go$$o(default(TIn); + } + """, MainDescription("TOut C.Goo(TIn arg) where TIn : Employee, new()")); } @@ -2784,29 +3173,33 @@ await TestInClassAsync( public async Task UnConstructedGenericMethodWithConstraintsAtInvocation() { await TestInClassAsync( -@"TOut Goo(TIn arg) where TIn : Employee -{ - Go$$o(default(TIn); -}", - - MainDescription("TOut C.Goo(TIn arg) where TIn : Employee")); + """ + TOut Goo(TIn arg) where TIn : Employee + { + Go$$o(default(TIn); + } + """, + + MainDescription("TOut C.Goo(TIn arg) where TIn : Employee")); } [Fact] public async Task GenericTypeWithConstraintsAtDeclaration() { await TestAsync( -@"public class Employee : IComparable -{ - public int CompareTo(Employee other) - { - throw new NotImplementedException(); - } -} + """ + public class Employee : IComparable + { + public int CompareTo(Employee other) + { + throw new NotImplementedException(); + } + } -class Emplo$$yeeList : IEnumerable where T : Employee, System.IComparable, new() -{ -}", + class Emplo$$yeeList : IEnumerable where T : Employee, System.IComparable, new() + { + } + """, MainDescription("class EmployeeList where T : Employee, System.IComparable, new()")); } @@ -2815,10 +3208,12 @@ public int CompareTo(Employee other) public async Task GenericType() { await TestAsync( -@"class T1 -{ - $$T11 i; -}", + """ + class T1 + { + $$T11 i; + } + """, MainDescription($"T11 {FeaturesResources.in_} T1")); } @@ -2826,10 +3221,12 @@ await TestAsync( public async Task GenericMethod() { await TestInClassAsync( -@"static void Meth1(T1 i) where T1 : struct -{ - $$T1 i; -}", + """ + static void Meth1(T1 i) where T1 : struct + { + $$T1 i; + } + """, MainDescription($"T1 {FeaturesResources.in_} C.Meth1 where T1 : struct")); } @@ -2837,8 +3234,10 @@ await TestInClassAsync( public async Task Var() { await TestInMethodAsync( -@"var x = new Exception(); -var y = $$x;", + """ + var x = new Exception(); + var y = $$x; + """, MainDescription($"({FeaturesResources.local_variable}) Exception x")); } @@ -2847,18 +3246,20 @@ public async Task NullableReference() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp8), -@"class A -{ -} -class B -{ - static void M() - { - A? x = null!; - var y = x; - $$y.ToString(); - } -}", + """ + class A + { + } + class B + { + static void M() + { + A? x = null!; + var y = x; + $$y.ToString(); + } + } + """, // https://github.com/dotnet/roslyn/issues/26198 public API should show inferred nullability MainDescription($"({FeaturesResources.local_variable}) A y")); } @@ -2866,21 +3267,23 @@ static void M() [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26648")] public async Task NullableReference_InMethod() { - var code = @" -class G -{ - void M() - { - C c; - c.Go$$o(); - } -} -public class C -{ - public string? Goo(IEnumerable arg) - { - } -}"; + var code = """ + + class G + { + void M() + { + C c; + c.Go$$o(); + } + } + public class C + { + public string? Goo(IEnumerable arg) + { + } + } + """; await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp8), code, MainDescription("string? C.Goo(IEnumerable arg)")); @@ -2890,29 +3293,37 @@ await TestWithOptionsAsync( public async Task NestedInGeneric() { await TestInMethodAsync( -@"List.Enu$$merator e;", + @"List.Enu$$merator e;", MainDescription("struct System.Collections.Generic.List.Enumerator"), - TypeParameterMap($"\r\nT {FeaturesResources.is_} int")); + TypeParameterMap($""" + + T {FeaturesResources.is_} int + """)); } [Fact] public async Task NestedGenericInGeneric() { await TestAsync( -@"class Outer -{ - class Inner - { - } + """ + class Outer + { + class Inner + { + } - static void M() - { - Outer.I$$nner e; - } -}", + static void M() + { + Outer.I$$nner e; + } + } + """, MainDescription("class Outer.Inner"), TypeParameterMap( - Lines($"\r\nT {FeaturesResources.is_} int", + Lines($""" + + T {FeaturesResources.is_} int + """, $"U {FeaturesResources.is_} string"))); } @@ -2920,15 +3331,17 @@ static void M() public async Task ObjectInitializer1() { await TestInClassAsync( -@"void M() -{ - var x = new test() { $$z = 5 }; -} + """ + void M() + { + var x = new test() { $$z = 5 }; + } -class test -{ - public int z; -}", + class test + { + public int z; + } + """, MainDescription($"({FeaturesResources.field}) int test.z")); } @@ -2936,18 +3349,20 @@ class test public async Task ObjectInitializer2() { await TestInMethodAsync( -@"class C -{ - void M() - { - var x = new test() { z = $$5 }; - } + """ + class C + { + void M() + { + var x = new test() { z = $$5 }; + } - class test - { - public int z; - } -}", + class test + { + public int z; + } + } + """, MainDescription("struct System.Int32")); } @@ -2955,14 +3370,16 @@ class test public async Task TypeArgument() { await TestAsync( -@"class C -{ - void M() - { - C variable; - $$variable = new C(); - } -}", + """ + class C + { + void M() + { + C variable; + $$variable = new C(); + } + } + """, MainDescription($"({FeaturesResources.local_variable}) C variable")); } @@ -2970,13 +3387,15 @@ void M() public async Task ForEachLoop_1() { await TestInMethodAsync( -@"int bb = 555; + """ + int bb = 555; -bb = bb + 1; -foreach (int cc in new int[]{ 1,2,3}){ -c$$c = 1; -bb = bb + 21; -}", + bb = bb + 1; + foreach (int cc in new int[]{ 1,2,3}){ + c$$c = 1; + bb = bb + 21; + } + """, MainDescription($"({FeaturesResources.local_variable}) int cc")); } @@ -2984,18 +3403,20 @@ await TestInMethodAsync( public async Task TryCatchFinally_1() { await TestInMethodAsync( -@"try - { - int aa = 555; - -a$$a = aa + 1; - } - catch (Exception ex) - { - } - finally - { - }", + """ + try + { + int aa = 555; + + a$$a = aa + 1; + } + catch (Exception ex) + { + } + finally + { + } + """, MainDescription($"({FeaturesResources.local_variable}) int aa")); } @@ -3003,17 +3424,19 @@ await TestInMethodAsync( public async Task TryCatchFinally_2() { await TestInMethodAsync( -@"try - { - } - catch (Exception ex) - { - var y = e$$x; -var z = y; - } - finally - { - }", + """ + try + { + } + catch (Exception ex) + { + var y = e$$x; + var z = y; + } + finally + { + } + """, MainDescription($"({FeaturesResources.local_variable}) Exception ex")); } @@ -3021,18 +3444,20 @@ await TestInMethodAsync( public async Task TryCatchFinally_3() { await TestInMethodAsync( -@"try - { - } - catch (Exception ex) - { - var aa = 555; - -aa = a$$a + 1; - } - finally - { - }", + """ + try + { + } + catch (Exception ex) + { + var aa = 555; + + aa = a$$a + 1; + } + finally + { + } + """, MainDescription($"({FeaturesResources.local_variable}) int aa")); } @@ -3040,18 +3465,20 @@ await TestInMethodAsync( public async Task TryCatchFinally_4() { await TestInMethodAsync( -@"try - { - } - catch (Exception ex) - { - } - finally - { - int aa = 555; - -aa = a$$a + 1; - }", + """ + try + { + } + catch (Exception ex) + { + } + finally + { + int aa = 555; + + aa = a$$a + 1; + } + """, MainDescription($"({FeaturesResources.local_variable}) int aa")); } @@ -3059,14 +3486,16 @@ await TestInMethodAsync( public async Task GenericVariable() { await TestAsync( -@"class C -{ - void M() - { - C variable; - var$$iable = new C(); - } -}", + """ + class C + { + void M() + { + C variable; + var$$iable = new C(); + } + } + """, MainDescription($"({FeaturesResources.local_variable}) C variable")); } @@ -3074,15 +3503,17 @@ void M() public async Task TestInstantiation() { await TestAsync( -@"using System.Collections.Generic; + """ + using System.Collections.Generic; -class Program -{ - static void Main(string[] args) - { - var p = new Dictio$$nary(); - } -}", + class Program + { + static void Main(string[] args) + { + var p = new Dictio$$nary(); + } + } + """, MainDescription($"Dictionary.Dictionary() (+ 5 {FeaturesResources.overloads_})")); } @@ -3090,18 +3521,20 @@ static void Main(string[] args) public async Task TestUsingAlias_Bug4141() { await TestAsync( -@"using X = A.C; + """ + using X = A.C; -class A -{ - public class C - { - } -} + class A + { + public class C + { + } + } -class D : X$$ -{ -}", + class D : X$$ + { + } + """, MainDescription(@"class A.C")); } @@ -3109,7 +3542,7 @@ class D : X$$ public async Task TestFieldOnDeclaration() { await TestInClassAsync( -@"DateTime fie$$ld;", + @"DateTime fie$$ld;", MainDescription($"({FeaturesResources.field}) DateTime C.field")); } @@ -3117,7 +3550,7 @@ await TestInClassAsync( public async Task TestGenericErrorFieldOnDeclaration() { await TestInClassAsync( -@"NonExistentType fi$$eld;", + @"NonExistentType fi$$eld;", MainDescription($"({FeaturesResources.field}) NonExistentType C.field")); } @@ -3125,10 +3558,13 @@ await TestInClassAsync( public async Task TestDelegateType() { await TestInClassAsync( -@"Fun$$c field;", + @"Fun$$c field;", MainDescription("delegate TResult System.Func(T arg)"), TypeParameterMap( - Lines($"\r\nT {FeaturesResources.is_} int", + Lines($""" + + T {FeaturesResources.is_} int + """, $"TResult {FeaturesResources.is_} string"))); } @@ -3136,16 +3572,18 @@ await TestInClassAsync( public async Task TestOnDelegateInvocation() { await TestAsync( -@"class Program -{ - delegate void D1(); + """ + class Program + { + delegate void D1(); - static void Main() - { - D1 d = Main; - $$d(); - } -}", + static void Main() + { + D1 d = Main; + $$d(); + } + } + """, MainDescription($"({FeaturesResources.local_variable}) D1 d")); } @@ -3153,26 +3591,30 @@ static void Main() public async Task TestOnArrayCreation1() { await TestAsync( -@"class Program -{ - static void Main() - { - int[] a = n$$ew int[0]; - } -}", MainDescription("int[]")); + """ + class Program + { + static void Main() + { + int[] a = n$$ew int[0]; + } + } + """, MainDescription("int[]")); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539240")] public async Task TestOnArrayCreation2() { await TestAsync( -@"class Program -{ - static void Main() - { - int[] a = new i$$nt[0]; - } -}", + """ + class Program + { + static void Main() + { + int[] a = new i$$nt[0]; + } + } + """, MainDescription("struct System.Int32")); } @@ -3180,14 +3622,16 @@ static void Main() public async Task Constructor_ImplicitObjectCreation() { await TestAsync( -@"class C -{ - static void Main() - { - C c = ne$$w(); - } -} -", + """ + class C + { + static void Main() + { + C c = ne$$w(); + } + } + + """, MainDescription("C.C()")); } @@ -3195,16 +3639,18 @@ static void Main() public async Task Constructor_ImplicitObjectCreation_WithParameters() { await TestAsync( -@"class C -{ - C(int i) { } - C(string s) { } - static void Main() - { - C c = ne$$w(1); - } -} -", + """ + class C + { + C(int i) { } + C(string s) { } + static void Main() + { + C c = ne$$w(1); + } + } + + """, MainDescription($"C.C(int i) (+ 1 {FeaturesResources.overload})")); } @@ -3212,16 +3658,18 @@ static void Main() public async Task TestIsNamedTypeAccessibleForErrorTypes() { await TestAsync( -@"sealed class B : A> -{ - protected sealed override B, A$$> N() - { - } -} + """ + sealed class B : A> + { + protected sealed override B, A$$> N() + { + } + } -internal class A -{ -}", + internal class A + { + } + """, MainDescription("class A")); } @@ -3229,15 +3677,17 @@ internal class A public async Task TestErrorType() { await TestAsync( -@"using Goo = Goo; + """ + using Goo = Goo; -class C -{ - void Main() - { - $$Goo - } -}", + class C + { + void Main() + { + $$Goo + } + } + """, MainDescription("Goo")); } @@ -3245,27 +3695,31 @@ void Main() public async Task TestShortDiscardInAssignment() { await TestAsync( -@"class C -{ - int M() - { - $$_ = M(); - } -}", - MainDescription($"({FeaturesResources.discard}) int _")); + """ + class C + { + int M() + { + $$_ = M(); + } + } + """, + MainDescription($"({FeaturesResources.discard}) int _")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16662")] public async Task TestUnderscoreLocalInAssignment() { await TestAsync( -@"class C -{ - int M() - { - var $$_ = M(); - } -}", + """ + class C + { + int M() + { + var $$_ = M(); + } + } + """, MainDescription($"({FeaturesResources.local_variable}) int _")); } @@ -3273,14 +3727,16 @@ int M() public async Task TestShortDiscardInOutVar() { await TestAsync( -@"class C -{ - void M(out int i) - { - M(out $$_); - i = 0; - } -}", + """ + class C + { + void M(out int i) + { + M(out $$_); + i = 0; + } + } + """, MainDescription($"({FeaturesResources.discard}) int _")); } @@ -3288,57 +3744,65 @@ void M(out int i) public async Task TestDiscardInOutVar() { await TestAsync( -@"class C -{ - void M(out int i) - { - M(out var $$_); - i = 0; - } -}"); // No quick info (see issue #16667) + """ + class C + { + void M(out int i) + { + M(out var $$_); + i = 0; + } + } + """); // No quick info (see issue #16667) } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16667")] public async Task TestDiscardInIsPattern() { await TestAsync( -@"class C -{ - void M() - { - if (3 is int $$_) { } - } -}"); // No quick info (see issue #16667) + """ + class C + { + void M() + { + if (3 is int $$_) { } + } + } + """); // No quick info (see issue #16667) } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/16667")] public async Task TestDiscardInSwitchPattern() { await TestAsync( -@"class C -{ - void M() - { - switch (3) - { - case int $$_: - return; - } - } -}"); // No quick info (see issue #16667) + """ + class C + { + void M() + { + switch (3) + { + case int $$_: + return; + } + } + } + """); // No quick info (see issue #16667) } [Fact] public async Task TestLambdaDiscardParameter_FirstDiscard() { await TestAsync( -@"class C -{ - void M() - { - System.Func f = ($$_, _) => 1; - } -}", + """ + class C + { + void M() + { + System.Func f = ($$_, _) => 1; + } + } + """, MainDescription($"({FeaturesResources.discard}) string _")); } @@ -3346,13 +3810,15 @@ void M() public async Task TestLambdaDiscardParameter_SecondDiscard() { await TestAsync( -@"class C -{ - void M() - { - System.Func f = (_, $$_) => 1; - } -}", + """ + class C + { + void M() + { + System.Func f = (_, $$_) => 1; + } + } + """, MainDescription($"({FeaturesResources.discard}) int _")); } @@ -3360,24 +3826,26 @@ void M() public async Task TestLiterals() { await TestAsync( -@"class MyClass -{ - MyClass() : this($$10) - { - intI = 2; - } + """ + class MyClass + { + MyClass() : this($$10) + { + intI = 2; + } - public MyClass(int i) - { - } + public MyClass(int i) + { + } - static int intI = 1; + static int intI = 1; - public static int Main() - { - return 1; - } -}", + public static int Main() + { + return 1; + } + } + """, MainDescription("struct System.Int32")); } @@ -3385,16 +3853,18 @@ public static int Main() public async Task TestErrorInForeach() { await TestAsync( -@"class C -{ - void Main() - { - foreach (int cc in null) - { - $$cc = 1; - } - } -}", + """ + class C + { + void Main() + { + foreach (int cc in null) + { + $$cc = 1; + } + } + } + """, MainDescription($"({FeaturesResources.local_variable}) int cc")); } @@ -3402,30 +3872,32 @@ void Main() public async Task TestQuickInfoOnEvent() { await TestAsync( -@"using System; + """ + using System; -public class SampleEventArgs -{ - public SampleEventArgs(string s) - { - Text = s; - } + public class SampleEventArgs + { + public SampleEventArgs(string s) + { + Text = s; + } - public String Text { get; private set; } -} + public String Text { get; private set; } + } -public class Publisher -{ - public delegate void SampleEventHandler(object sender, SampleEventArgs e); + public class Publisher + { + public delegate void SampleEventHandler(object sender, SampleEventArgs e); - public event SampleEventHandler SampleEvent; + public event SampleEventHandler SampleEvent; - protected virtual void RaiseSampleEvent() - { - if (Sam$$pleEvent != null) - SampleEvent(this, new SampleEventArgs(""Hello"")); - } -}", + protected virtual void RaiseSampleEvent() + { + if (Sam$$pleEvent != null) + SampleEvent(this, new SampleEventArgs("Hello")); + } + } + """, MainDescription("SampleEventHandler Publisher.SampleEvent")); } @@ -3454,28 +3926,30 @@ public async Task TestEventMinusEqualsOperator() public async Task TestQuickInfoOnExtensionMethod() { await TestWithOptionsAsync(Options.Regular, -@"using System; -using System.Collections.Generic; -using System.Linq; + """ + using System; + using System.Collections.Generic; + using System.Linq; -class Program -{ - static void Main(string[] args) - { - int[] values = { - 1 - }; - bool isArray = 7.I$$n(values); - } -} + class Program + { + static void Main(string[] args) + { + int[] values = { + 1 + }; + bool isArray = 7.I$$n(values); + } + } -public static class MyExtensions -{ - public static bool In(this T o, IEnumerable items) - { - return true; - } -}", + public static class MyExtensions + { + public static bool In(this T o, IEnumerable items) + { + return true; + } + } + """, MainDescription($"({CSharpFeaturesResources.extension}) bool int.In(IEnumerable items)")); } @@ -3483,31 +3957,33 @@ public static bool In(this T o, IEnumerable items) public async Task TestQuickInfoOnExtensionMethodOverloads() { await TestWithOptionsAsync(Options.Regular, -@"using System; -using System.Linq; + """ + using System; + using System.Linq; -class Program -{ - static void Main(string[] args) - { - ""1"".Test$$Ext(); - } -} + class Program + { + static void Main(string[] args) + { + "1".Test$$Ext(); + } + } -public static class Ex -{ - public static void TestExt(this T ex) - { - } + public static class Ex + { + public static void TestExt(this T ex) + { + } - public static void TestExt(this T ex, T arg) - { - } + public static void TestExt(this T ex, T arg) + { + } - public static void TestExt(this string ex, int arg) - { - } -}", + public static void TestExt(this string ex, int arg) + { + } + } + """, MainDescription($"({CSharpFeaturesResources.extension}) void string.TestExt() (+ 2 {FeaturesResources.overloads_})")); } @@ -3515,31 +3991,33 @@ public static void TestExt(this string ex, int arg) public async Task TestQuickInfoOnExtensionMethodOverloads2() { await TestWithOptionsAsync(Options.Regular, -@"using System; -using System.Linq; + """ + using System; + using System.Linq; -class Program -{ - static void Main(string[] args) - { - ""1"".Test$$Ext(); - } -} + class Program + { + static void Main(string[] args) + { + "1".Test$$Ext(); + } + } -public static class Ex -{ - public static void TestExt(this T ex) - { - } + public static class Ex + { + public static void TestExt(this T ex) + { + } - public static void TestExt(this T ex, T arg) - { - } + public static void TestExt(this T ex, T arg) + { + } - public static void TestExt(this int ex, int arg) - { - } -}", + public static void TestExt(this int ex, int arg) + { + } + } + """, MainDescription($"({CSharpFeaturesResources.extension}) void string.TestExt() (+ 1 {FeaturesResources.overload})")); } @@ -3547,17 +4025,19 @@ public static void TestExt(this int ex, int arg) public async Task Query1() { await TestAsync( -@"using System.Linq; + """ + using System.Linq; -class C -{ - void M() - { - var q = from n in new int[] { 1, 2, 3, 4, 5 } + class C + { + void M() + { + var q = from n in new int[] { 1, 2, 3, 4, 5 } - select $$n; - } -}", + select $$n; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) int n")); } @@ -3565,17 +4045,19 @@ void M() public async Task Query2() { await TestAsync( -@"using System.Linq; + """ + using System.Linq; -class C -{ - void M() - { - var q = from n$$ in new int[] { 1, 2, 3, 4, 5 } + class C + { + void M() + { + var q = from n$$ in new int[] { 1, 2, 3, 4, 5 } - select n; - } -}", + select n; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) int n")); } @@ -3583,15 +4065,17 @@ void M() public async Task Query3() { await TestAsync( -@"class C -{ - void M() - { - var q = from n in new int[] { 1, 2, 3, 4, 5 } + """ + class C + { + void M() + { + var q = from n in new int[] { 1, 2, 3, 4, 5 } - select $$n; - } -}", + select $$n; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) ? n")); } @@ -3599,15 +4083,17 @@ void M() public async Task Query4() { await TestAsync( -@"class C -{ - void M() - { - var q = from n$$ in new int[] { 1, 2, 3, 4, 5 } + """ + class C + { + void M() + { + var q = from n$$ in new int[] { 1, 2, 3, 4, 5 } - select n; - } -}", + select n; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) ? n")); } @@ -3615,35 +4101,39 @@ void M() public async Task Query5() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -class C -{ - void M() - { - var q = from n in new List() - select $$n; - } -}", - MainDescription($"({FeaturesResources.range_variable}) object n")); - } + class C + { + void M() + { + var q = from n in new List() + select $$n; + } + } + """, + MainDescription($"({FeaturesResources.range_variable}) object n")); + } [Fact] public async Task Query6() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -class C -{ - void M() - { - var q = from n$$ in new List() - select n; - } -}", + class C + { + void M() + { + var q = from n$$ in new List() + select n; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) object n")); } @@ -3651,17 +4141,19 @@ void M() public async Task Query7() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -class C -{ - void M() - { - var q = from int n in new List() - select $$n; - } -}", + class C + { + void M() + { + var q = from int n in new List() + select $$n; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) int n")); } @@ -3669,17 +4161,19 @@ void M() public async Task Query8() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -class C -{ - void M() - { - var q = from int n$$ in new List() - select n; - } -}", + class C + { + void M() + { + var q = from int n$$ in new List() + select n; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) int n")); } @@ -3687,18 +4181,20 @@ void M() public async Task Query9() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -class C -{ - void M() - { - var q = from x$$ in new List>() - from y in x - select y; - } -}", + class C + { + void M() + { + var q = from x$$ in new List>() + from y in x + select y; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) List x")); } @@ -3706,18 +4202,20 @@ from y in x public async Task Query10() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -class C -{ - void M() - { - var q = from x in new List>() - from y in $$x - select y; - } -}", + class C + { + void M() + { + var q = from x in new List>() + from y in $$x + select y; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) List x")); } @@ -3725,18 +4223,20 @@ from y in $$x public async Task Query11() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -class C -{ - void M() - { - var q = from x in new List>() - from y$$ in x - select y; - } -}", + class C + { + void M() + { + var q = from x in new List>() + from y$$ in x + select y; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) int y")); } @@ -3744,18 +4244,20 @@ from y$$ in x public async Task Query12() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -class C -{ - void M() - { - var q = from x in new List>() - from y in x - select $$y; - } -}", + class C + { + void M() + { + var q = from x in new List>() + from y in x + select $$y; + } + } + """, MainDescription($"({FeaturesResources.range_variable}) int y")); } @@ -3763,10 +4265,12 @@ from y in x public async Task QueryMethodinfoSelectMappedEnumerable() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$select i; -", + """ + + var q = from i in new int[0] + $$select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Select(Func selector)")); } @@ -3774,10 +4278,12 @@ await TestInMethodAsync( public async Task QueryMethodinfoSelectMappedQueryable() { await TestInMethodAsync( -@" - var q = from i in new int[0].AsQueryable() - $$select i; -", + """ + + var q = from i in new int[0].AsQueryable() + $$select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IQueryable IQueryable.Select(System.Linq.Expressions.Expression> selector)")); } @@ -3785,26 +4291,28 @@ await TestInMethodAsync( public async Task QueryMethodinfoSelectMappedCustom() { await TestAsync( -@" -using System; -using System.Linq; + """ -namespace N { - public static class LazyExt - { - public static Lazy Select(this Lazy source, Func selector) => new Lazy(() => selector(source.Value)); - } - public class C - { - public void M() - { - var lazy = new Lazy(); - var q = from i in lazy - $$select i; - } - } -} -", + using System; + using System.Linq; + + namespace N { + public static class LazyExt + { + public static Lazy Select(this Lazy source, Func selector) => new Lazy(() => selector(source.Value)); + } + public class C + { + public void M() + { + var lazy = new Lazy(); + var q = from i in lazy + $$select i; + } + } + } + + """, MainDescription($"({CSharpFeaturesResources.extension}) Lazy Lazy.Select(Func selector)")); } @@ -3812,37 +4320,45 @@ public void M() public async Task QueryMethodinfoSelectNotMapped() { await TestInMethodAsync( -@" - var q = from i in new int[0] - where true - $$select i; -"); + """ + + var q = from i in new int[0] + where true + $$select i; + + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoLet() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$let j = true - select i; -", + """ + + var q = from i in new int[0] + $$let j = true + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable<'a> IEnumerable.Select(Func selector)"), - AnonymousTypes($@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} new {{ int i, bool j }}")); + AnonymousTypes($$""" + + {{FeaturesResources.Types_colon}} + 'a {{FeaturesResources.is_}} new { int i, bool j } + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoWhere() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$where true - select i; -", + """ + + var q = from i in new int[0] + $$where true + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Where(Func predicate)")); } @@ -3850,11 +4366,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoOrderByOneProperty() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$orderby i - select i; -", + """ + + var q = from i in new int[0] + $$orderby i + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IEnumerable.OrderBy(Func keySelector)")); } @@ -3862,11 +4380,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoOrderByOnePropertyWithOrdering1() { await TestInMethodAsync( -@" - var q = from i in new int[0] - orderby i $$ascending - select i; -", + """ + + var q = from i in new int[0] + orderby i $$ascending + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IEnumerable.OrderBy(Func keySelector)")); } @@ -3874,22 +4394,26 @@ orderby i $$ascending public async Task QueryMethodinfoOrderByOnePropertyWithOrdering2() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$orderby i ascending - select i; -"); + """ + + var q = from i in new int[0] + $$orderby i ascending + select i; + + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoOrderByTwoPropertiesWithComma1() { await TestInMethodAsync( -@" - var q = from i in new int[0] - orderby i$$, i - select i; -", + """ + + var q = from i in new int[0] + orderby i$$, i + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IOrderedEnumerable.ThenBy(Func keySelector)")); } @@ -3897,11 +4421,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoOrderByTwoPropertiesWithComma2() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$orderby i, i - select i; -", + """ + + var q = from i in new int[0] + $$orderby i, i + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IEnumerable.OrderBy(Func keySelector)")); } @@ -3909,11 +4435,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoOrderByTwoPropertiesWithOrdering1() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$orderby i, i ascending - select i; -", + """ + + var q = from i in new int[0] + $$orderby i, i ascending + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IEnumerable.OrderBy(Func keySelector)")); } @@ -3921,22 +4449,26 @@ await TestInMethodAsync( public async Task QueryMethodinfoOrderByTwoPropertiesWithOrdering2() { await TestInMethodAsync( -@" - var q = from i in new int[0] - orderby i,$$ i ascending - select i; -"); + """ + + var q = from i in new int[0] + orderby i,$$ i ascending + select i; + + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoOrderByTwoPropertiesWithOrdering3() { await TestInMethodAsync( -@" - var q = from i in new int[0] - orderby i, i $$ascending - select i; -", + """ + + var q = from i in new int[0] + orderby i, i $$ascending + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IOrderedEnumerable.ThenBy(Func keySelector)")); } @@ -3944,22 +4476,26 @@ await TestInMethodAsync( public async Task QueryMethodinfoOrderByTwoPropertiesWithOrderingOnEach1() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$orderby i ascending, i ascending - select i; -"); + """ + + var q = from i in new int[0] + $$orderby i ascending, i ascending + select i; + + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoOrderByTwoPropertiesWithOrderingOnEach2() { await TestInMethodAsync( -@" - var q = from i in new int[0] - orderby i $$ascending, i ascending - select i; -", + """ + + var q = from i in new int[0] + orderby i $$ascending, i ascending + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IEnumerable.OrderBy(Func keySelector)")); } @@ -3967,22 +4503,26 @@ await TestInMethodAsync( public async Task QueryMethodinfoOrderByTwoPropertiesWithOrderingOnEach3() { await TestInMethodAsync( -@" - var q = from i in new int[0] - orderby i ascending ,$$ i ascending - select i; -"); + """ + + var q = from i in new int[0] + orderby i ascending ,$$ i ascending + select i; + + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoOrderByTwoPropertiesWithOrderingOnEach4() { await TestInMethodAsync( -@" - var q = from i in new int[0] - orderby i ascending, i $$ascending - select i; -", + """ + + var q = from i in new int[0] + orderby i ascending, i $$ascending + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IOrderedEnumerable.ThenBy(Func keySelector)")); } @@ -3990,11 +4530,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoOrderByIncomplete() { await TestInMethodAsync( -@" - var q = from i in new int[0] - where i > 0 - orderby$$ -", + """ + + var q = from i in new int[0] + where i > 0 + orderby$$ + + """, MainDescription($"({CSharpFeaturesResources.extension}) IOrderedEnumerable IEnumerable.OrderBy(Func keySelector)")); } @@ -4002,11 +4544,13 @@ where i > 0 public async Task QueryMethodinfoSelectMany1() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - $$from i2 in new int[0] - select i1; -", + """ + + var q = from i1 in new int[0] + $$from i2 in new int[0] + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.SelectMany(Func> collectionSelector, Func resultSelector)")); } @@ -4014,11 +4558,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoSelectMany2() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - from i2 $$in new int[0] - select i1; -", + """ + + var q = from i1 in new int[0] + from i2 $$in new int[0] + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.SelectMany(Func> collectionSelector, Func resultSelector)")); } @@ -4026,10 +4572,12 @@ await TestInMethodAsync( public async Task QueryMethodinfoGroupBy1() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$group i by i; -", + """ + + var q = from i in new int[0] + $$group i by i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable> IEnumerable.GroupBy(Func keySelector)")); } @@ -4037,10 +4585,12 @@ await TestInMethodAsync( public async Task QueryMethodinfoGroupBy2() { await TestInMethodAsync( -@" - var q = from i in new int[0] - group i $$by i; -", + """ + + var q = from i in new int[0] + group i $$by i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable> IEnumerable.GroupBy(Func keySelector)")); } @@ -4048,11 +4598,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoGroupByInto() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$group i by i into g - select g; -", + """ + + var q = from i in new int[0] + $$group i by i into g + select g; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable> IEnumerable.GroupBy(Func keySelector)")); } @@ -4060,11 +4612,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoJoin1() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - $$join i2 in new int[0] on i1 equals i2 - select i1; -", + """ + + var q = from i1 in new int[0] + $$join i2 in new int[0] on i1 equals i2 + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Join(IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector)")); } @@ -4072,11 +4626,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoJoin2() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - join i2 $$in new int[0] on i1 equals i2 - select i1; -", + """ + + var q = from i1 in new int[0] + join i2 $$in new int[0] on i1 equals i2 + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Join(IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector)")); } @@ -4084,11 +4640,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoJoin3() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - join i2 in new int[0] $$on i1 equals i2 - select i1; -", + """ + + var q = from i1 in new int[0] + join i2 in new int[0] $$on i1 equals i2 + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Join(IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector)")); } @@ -4096,11 +4654,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoJoin4() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - join i2 in new int[0] on i1 $$equals i2 - select i1; -", + """ + + var q = from i1 in new int[0] + join i2 in new int[0] on i1 $$equals i2 + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Join(IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector)")); } @@ -4108,11 +4668,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoJoinInto1() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - $$join i2 in new int[0] on i1 equals i2 into g - select g; -", + """ + + var q = from i1 in new int[0] + $$join i2 in new int[0] on i1 equals i2 into g + select g; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable> IEnumerable.GroupJoin>(IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func, IEnumerable> resultSelector)")); } @@ -4120,31 +4682,37 @@ await TestInMethodAsync( public async Task QueryMethodinfoJoinInto2() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - join i2 in new int[0] on i1 equals i2 $$into g - select g; -"); + """ + + var q = from i1 in new int[0] + join i2 in new int[0] on i1 equals i2 $$into g + select g; + + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoFromMissing() { await TestInMethodAsync( -@" - var q = $$from i in new int[0] - select i; -"); + """ + + var q = $$from i in new int[0] + select i; + + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23394")] public async Task QueryMethodinfoRangeVariableSimple1() { await TestInMethodAsync( -@" - var q = $$from double i in new int[0] - select i; -", + """ + + var q = $$from double i in new int[0] + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable System.Collections.IEnumerable.Cast()")); } @@ -4152,10 +4720,12 @@ await TestInMethodAsync( public async Task QueryMethodinfoRangeVariableSimple2() { await TestInMethodAsync( -@" - var q = from double i $$in new int[0] - select i; -", + """ + + var q = from double i $$in new int[0] + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable System.Collections.IEnumerable.Cast()")); } @@ -4163,11 +4733,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoRangeVariableSelectMany1() { await TestInMethodAsync( -@" - var q = from i in new int[0] - $$from double d in new int[0] - select i; -", + """ + + var q = from i in new int[0] + $$from double d in new int[0] + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.SelectMany(Func> collectionSelector, Func resultSelector)")); } @@ -4175,11 +4747,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoRangeVariableSelectMany2() { await TestInMethodAsync( -@" - var q = from i in new int[0] - from double d $$in new int[0] - select i; -", + """ + + var q = from i in new int[0] + from double d $$in new int[0] + select i; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable System.Collections.IEnumerable.Cast()")); } @@ -4187,11 +4761,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoRangeVariableJoin1() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - $$join int i2 in new double[0] on i1 equals i2 - select i1; -", + """ + + var q = from i1 in new int[0] + $$join int i2 in new double[0] on i1 equals i2 + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Join(IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector)")); } @@ -4199,11 +4775,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoRangeVariableJoin2() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - join int i2 $$in new double[0] on i1 equals i2 - select i1; -", + """ + + var q = from i1 in new int[0] + join int i2 $$in new double[0] on i1 equals i2 + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable System.Collections.IEnumerable.Cast()")); } @@ -4211,11 +4789,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoRangeVariableJoin3() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - join int i2 in new double[0] $$on i1 equals i2 - select i1; -", + """ + + var q = from i1 in new int[0] + join int i2 in new double[0] $$on i1 equals i2 + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Join(IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector)")); } @@ -4223,11 +4803,13 @@ await TestInMethodAsync( public async Task QueryMethodinfoRangeVariableJoin4() { await TestInMethodAsync( -@" - var q = from i1 in new int[0] - join int i2 in new double[0] on i1 $$equals i2 - select i1; -", + """ + + var q = from i1 in new int[0] + join int i2 in new double[0] on i1 $$equals i2 + select i1; + + """, MainDescription($"({CSharpFeaturesResources.extension}) IEnumerable IEnumerable.Join(IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector)")); } @@ -4235,16 +4817,18 @@ await TestInMethodAsync( public async Task TestErrorGlobal() { await TestAsync( -@"extern alias global; + """ + extern alias global; -class myClass -{ - static int Main() - { - $$global::otherClass oc = new global::otherClass(); - return 0; - } -}", + class myClass + { + static int Main() + { + $$global::otherClass oc = new global::otherClass(); + return 0; + } + } + """, MainDescription("")); } @@ -4252,12 +4836,14 @@ static int Main() public async Task DoNotRemoveAttributeSuffixAndProduceInvalidIdentifier1() { await TestAsync( -@"using System; + """ + using System; -class classAttribute : Attribute -{ - private classAttribute x$$; -}", + class classAttribute : Attribute + { + private classAttribute x$$; + } + """, MainDescription($"({FeaturesResources.field}) classAttribute classAttribute.x")); } @@ -4265,12 +4851,14 @@ class classAttribute : Attribute public async Task DoNotRemoveAttributeSuffix2() { await TestAsync( -@"using System; + """ + using System; -class class1Attribute : Attribute -{ - private class1Attribute x$$; -}", + class class1Attribute : Attribute + { + private class1Attribute x$$; + } + """, MainDescription($"({FeaturesResources.field}) class1Attribute class1Attribute.x")); } @@ -4278,21 +4866,23 @@ class class1Attribute : Attribute public async Task AttributeQuickInfoBindsToClassTest() { await TestAsync( -@"using System; + """ + using System; -/// -/// class comment -/// -[Some$$] -class SomeAttribute : Attribute -{ - /// - /// ctor comment - /// - public SomeAttribute() - { - } -}", + /// + /// class comment + /// + [Some$$] + class SomeAttribute : Attribute + { + /// + /// ctor comment + /// + public SomeAttribute() + { + } + } + """, Documentation("class comment")); } @@ -4300,21 +4890,23 @@ public SomeAttribute() public async Task AttributeConstructorQuickInfo() { await TestAsync( -@"using System; + """ + using System; -/// -/// class comment -/// -class SomeAttribute : Attribute -{ - /// - /// ctor comment - /// - public SomeAttribute() - { - var s = new Some$$Attribute(); - } -}", + /// + /// class comment + /// + class SomeAttribute : Attribute + { + /// + /// ctor comment + /// + public SomeAttribute() + { + var s = new Some$$Attribute(); + } + } + """, Documentation("ctor comment")); } @@ -4322,12 +4914,14 @@ public SomeAttribute() public async Task TestLabel() { await TestInClassAsync( -@"void M() -{ -Goo: - int Goo; - goto Goo$$; -}", + """ + void M() + { + Goo: + int Goo; + goto Goo$$; + } + """, MainDescription($"({FeaturesResources.label}) Goo")); } @@ -4335,16 +4929,18 @@ await TestInClassAsync( public async Task TestUnboundGeneric() { await TestAsync( -@"using System; -using System.Collections.Generic; + """ + using System; + using System.Collections.Generic; -class C -{ - void M() - { - Type t = typeof(L$$ist<>); - } -}", + class C + { + void M() + { + Type t = typeof(L$$ist<>); + } + } + """, MainDescription("class System.Collections.Generic.List"), NoTypeParameterMap); } @@ -4353,19 +4949,23 @@ void M() public async Task TestAnonymousTypeNew1() { await TestAsync( -@"class C -{ - void M() - { - var v = $$new { }; - } -}", + """ + class C + { + void M() + { + var v = $$new { }; + } + } + """, MainDescription(@"AnonymousType 'a"), NoTypeParameterMap, AnonymousTypes( -$@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} new {{ }}")); + $$""" + + {{FeaturesResources.Types_colon}} + 'a {{FeaturesResources.is_}} new { } + """)); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543873")] @@ -4374,68 +4974,82 @@ public async Task TestNestedAnonymousType() // verify nested anonymous types are listed in the same order for different properties // verify first property await TestInMethodAsync( -@"var x = new[] { new { Name = ""BillG"", Address = new { Street = ""1 Microsoft Way"", Zip = ""98052"" } } }; + """ + var x = new[] { new { Name = "BillG", Address = new { Street = "1 Microsoft Way", Zip = "98052" } } }; -x[0].$$Address", + x[0].$$Address + """, MainDescription(@"'b 'a.Address { get; }"), NoTypeParameterMap, AnonymousTypes( -$@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} new {{ string Name, 'b Address }} - 'b {FeaturesResources.is_} new {{ string Street, string Zip }}")); + $$""" + + {{FeaturesResources.Types_colon}} + 'a {{FeaturesResources.is_}} new { string Name, 'b Address } + 'b {{FeaturesResources.is_}} new { string Street, string Zip } + """)); // verify second property await TestInMethodAsync( -@"var x = new[] { new { Name = ""BillG"", Address = new { Street = ""1 Microsoft Way"", Zip = ""98052"" } } }; + """ + var x = new[] { new { Name = "BillG", Address = new { Street = "1 Microsoft Way", Zip = "98052" } } }; -x[0].$$Name", + x[0].$$Name + """, MainDescription(@"string 'a.Name { get; }"), NoTypeParameterMap, AnonymousTypes( -$@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} new {{ string Name, 'b Address }} - 'b {FeaturesResources.is_} new {{ string Street, string Zip }}")); + $$""" + + {{FeaturesResources.Types_colon}} + 'a {{FeaturesResources.is_}} new { string Name, 'b Address } + 'b {{FeaturesResources.is_}} new { string Street, string Zip } + """)); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543183")] public async Task TestAssignmentOperatorInAnonymousType() { await TestAsync( -@"class C -{ - void M() - { - var a = new { A $$= 0 }; - } -}"); + """ + class C + { + void M() + { + var a = new { A $$= 0 }; + } + } + """); } [Fact, WorkItem(10731, "DevDiv_Projects/Roslyn")] public async Task TestErrorAnonymousTypeDoesntShow() { await TestInMethodAsync( -@"var a = new { new { N = 0 }.N, new { } }.$$N;", + @"var a = new { new { N = 0 }.N, new { } }.$$N;", MainDescription(@"int 'a.N { get; }"), NoTypeParameterMap, AnonymousTypes( -$@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} new {{ int N }}")); + $$""" + + {{FeaturesResources.Types_colon}} + 'a {{FeaturesResources.is_}} new { int N } + """)); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543553")] public async Task TestArrayAssignedToVar() { await TestAsync( -@"class C -{ - static void M(string[] args) - { - v$$ar a = args; - } -}", + """ + class C + { + static void M(string[] args) + { + v$$ar a = args; + } + } + """, MainDescription("string[]")); } @@ -4443,23 +5057,25 @@ static void M(string[] args) public async Task ColorColorRangeVariable() { await TestAsync( -@"using System.Collections.Generic; -using System.Linq; + """ + using System.Collections.Generic; + using System.Linq; -namespace N1 -{ - class yield - { - public static IEnumerable Bar() - { - foreach (yield yield in from yield in new yield[0] - select y$$ield) + namespace N1 { - yield return yield; + class yield + { + public static IEnumerable Bar() + { + foreach (yield yield in from yield in new yield[0] + select y$$ield) + { + yield return yield; + } + } + } } - } - } -}", + """, MainDescription($"({FeaturesResources.range_variable}) N1.yield yield")); } @@ -4467,26 +5083,28 @@ public static IEnumerable Bar() public async Task QuickInfoOnOperator() { await TestAsync( -@"using System.Collections.Generic; + """ + using System.Collections.Generic; -class Program -{ - static void Main(string[] args) - { - var v = new Program() $$+ string.Empty; - } + class Program + { + static void Main(string[] args) + { + var v = new Program() $$+ string.Empty; + } - public static implicit operator Program(string s) - { - return null; - } + public static implicit operator Program(string s) + { + return null; + } - public static IEnumerable operator +(Program p1, Program p2) - { - yield return p1; - yield return p2; - } -}", + public static IEnumerable operator +(Program p1, Program p2) + { + yield return p1; + yield return p2; + } + } + """, MainDescription("IEnumerable Program.operator +(Program p1, Program p2)")); } @@ -4494,9 +5112,11 @@ public static implicit operator Program(string s) public async Task TestConstantField() { await TestAsync( -@"class C -{ - const int $$F = 1;", + """ + class C + { + const int $$F = 1; + """, MainDescription($"({FeaturesResources.constant}) int C.F = 1")); } @@ -4504,9 +5124,11 @@ await TestAsync( public async Task TestMultipleConstantFields() { await TestAsync( -@"class C -{ - public const double X = 1.0, Y = 2.0, $$Z = 3.5;", + """ + class C + { + public const double X = 1.0, Y = 2.0, $$Z = 3.5; + """, MainDescription($"({FeaturesResources.constant}) double C.Z = 3.5")); } @@ -4514,16 +5136,18 @@ await TestAsync( public async Task TestConstantDependencies() { await TestAsync( -@"class A -{ - public const int $$X = B.Z + 1; - public const int Y = 10; -} + """ + class A + { + public const int $$X = B.Z + 1; + public const int Y = 10; + } -class B -{ - public const int Z = A.Y + 1; -}", + class B + { + public const int Z = A.Y + 1; + } + """, MainDescription($"({FeaturesResources.constant}) int A.X = B.Z + 1")); } @@ -4531,15 +5155,17 @@ class B public async Task TestConstantCircularDependencies() { await TestAsync( -@"class A -{ - public const int X = B.Z + 1; -} + """ + class A + { + public const int X = B.Z + 1; + } -class B -{ - public const int Z$$ = A.X + 1; -}", + class B + { + public const int Z$$ = A.X + 1; + } + """, MainDescription($"({FeaturesResources.constant}) int B.Z = A.X + 1")); } @@ -4547,10 +5173,12 @@ class B public async Task TestConstantOverflow() { await TestAsync( -@"class B -{ - public const int Z$$ = int.MaxValue + 1; -}", + """ + class B + { + public const int Z$$ = int.MaxValue + 1; + } + """, MainDescription($"({FeaturesResources.constant}) int B.Z = int.MaxValue + 1")); } @@ -4558,10 +5186,12 @@ await TestAsync( public async Task TestConstantOverflowInUncheckedContext() { await TestAsync( -@"class B -{ - public const int Z$$ = unchecked(int.MaxValue + 1); -}", + """ + class B + { + public const int Z$$ = unchecked(int.MaxValue + 1); + } + """, MainDescription($"({FeaturesResources.constant}) int B.Z = unchecked(int.MaxValue + 1)")); } @@ -4569,24 +5199,26 @@ await TestAsync( public async Task TestEnumInConstantField() { await TestAsync( -@"public class EnumTest -{ - enum Days - { - Sun, - Mon, - Tue, - Wed, - Thu, - Fri, - Sat - }; - - static void Main() - { - const int $$x = (int)Days.Sun; - } -}", + """ + public class EnumTest + { + enum Days + { + Sun, + Mon, + Tue, + Wed, + Thu, + Fri, + Sat + }; + + static void Main() + { + const int $$x = (int)Days.Sun; + } + } + """, MainDescription($"({FeaturesResources.local_constant}) int x = (int)Days.Sun")); } @@ -4594,24 +5226,26 @@ static void Main() public async Task TestConstantInDefaultExpression() { await TestAsync( -@"public class EnumTest -{ - enum Days - { - Sun, - Mon, - Tue, - Wed, - Thu, - Fri, - Sat - }; - - static void Main() - { - const Days $$x = default(Days); - } -}", + """ + public class EnumTest + { + enum Days + { + Sun, + Mon, + Tue, + Wed, + Thu, + Fri, + Sat + }; + + static void Main() + { + const Days $$x = default(Days); + } + } + """, MainDescription($"({FeaturesResources.local_constant}) Days x = default(Days)")); } @@ -4619,10 +5253,12 @@ static void Main() public async Task TestConstantParameter() { await TestAsync( -@"class C -{ - void Bar(int $$b = 1); -}", + """ + class C + { + void Bar(int $$b = 1); + } + """, MainDescription($"({FeaturesResources.parameter}) int b = 1")); } @@ -4630,12 +5266,14 @@ await TestAsync( public async Task TestConstantLocal() { await TestAsync( -@"class C -{ - void Bar() - { - const int $$loc = 1; - }", + """ + class C + { + void Bar() + { + const int $$loc = 1; + } + """, MainDescription($"({FeaturesResources.local_constant}) int loc = 1")); } @@ -4643,7 +5281,7 @@ void Bar() public async Task TestErrorType1() { await TestInMethodAsync( -@"var $$v1 = new Goo();", + @"var $$v1 = new Goo();", MainDescription($"({FeaturesResources.local_variable}) Goo v1")); } @@ -4651,7 +5289,7 @@ await TestInMethodAsync( public async Task TestErrorType2() { await TestInMethodAsync( -@"var $$v1 = v1;", + @"var $$v1 = v1;", MainDescription($"({FeaturesResources.local_variable}) var v1")); } @@ -4659,7 +5297,7 @@ await TestInMethodAsync( public async Task TestErrorType3() { await TestInMethodAsync( -@"var $$v1 = new Goo();", + @"var $$v1 = new Goo();", MainDescription($"({FeaturesResources.local_variable}) Goo v1")); } @@ -4667,7 +5305,7 @@ await TestInMethodAsync( public async Task TestErrorType4() { await TestInMethodAsync( -@"var $$v1 = &(x => x);", + @"var $$v1 = &(x => x);", MainDescription($"({FeaturesResources.local_variable}) ?* v1")); } @@ -4689,17 +5327,19 @@ public async Task TestErrorType6() public async Task TestErrorType7() { await TestInClassAsync( -@"class C -{ - void Method() - { - } + """ + class C + { + void Method() + { + } - void Goo() - { - var $$v1 = MethodGroup; - } -}", + void Goo() + { + var $$v1 = MethodGroup; + } + } + """, MainDescription($"({FeaturesResources.local_variable}) ? v1")); } @@ -4714,7 +5354,7 @@ await TestInMethodAsync("var $$v1 = Unknown", public async Task TestDelegateSpecialTypes() { await TestAsync( -@"delegate void $$F(int x);", + @"delegate void $$F(int x);", MainDescription("delegate void F(int x)")); } @@ -4722,12 +5362,14 @@ await TestAsync( public async Task TestNullPointerParameter() { await TestAsync( -@"class C -{ - unsafe void $$Goo(int* x = null) - { - } -}", + """ + class C + { + unsafe void $$Goo(int* x = null) + { + } + } + """, MainDescription("void C.Goo([int* x = null])")); } @@ -4742,12 +5384,14 @@ public async Task TestLetIdentifier1() public async Task TestNullableDefaultValue() { await TestAsync( -@"class Test -{ - void $$Method(int? t1 = null) - { - } -}", + """ + class Test + { + void $$Method(int? t1 = null) + { + } + } + """, MainDescription("void Test.Method([int? t1 = null])")); } @@ -4755,30 +5399,36 @@ await TestAsync( public async Task TestInvalidParameterInitializer() { await TestAsync( -@"class Program -{ - void M1(float $$j1 = ""Hello"" -+ -""World"") - { - } -}", - MainDescription($@"({FeaturesResources.parameter}) float j1 = ""Hello"" + ""World""")); + """ + class Program + { + void M1(float $$j1 = "Hello" + + + "World") + { + } + } + """, + MainDescription($""" + ({FeaturesResources.parameter}) float j1 = "Hello" + "World" + """)); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545230")] public async Task TestComplexConstLocal() { await TestAsync( -@"class Program -{ - void Main() - { - const int MEGABYTE = 1024 * - 1024 + true; - Blah($$MEGABYTE); - } -}", + """ + class Program + { + void Main() + { + const int MEGABYTE = 1024 * + 1024 + true; + Blah($$MEGABYTE); + } + } + """, MainDescription($@"({FeaturesResources.local_constant}) int MEGABYTE = 1024 * 1024 + true")); } @@ -4786,17 +5436,19 @@ void Main() public async Task TestComplexConstField() { await TestAsync( -@"class Program -{ - const int a = true - - - false; + """ + class Program + { + const int a = true + - + false; - void Main() - { - Goo($$a); - } -}", + void Main() + { + Goo($$a); + } + } + """, MainDescription($"({FeaturesResources.constant}) int Program.a = true - false")); } @@ -4804,26 +5456,30 @@ void Main() public async Task TestTypeParameterCrefDoesNotHaveQuickInfo() { await TestAsync( -@"class C -{ - /// - static void Main(string[] args) - { - } -}"); + """ + class C + { + /// + static void Main(string[] args) + { + } + } + """); } [Fact] public async Task TestCref1() { await TestAsync( -@"class Program -{ - /// - static void Main(string[] args) - { - } -}", + """ + class Program + { + /// + static void Main(string[] args) + { + } + } + """, MainDescription(@"void Program.Main(string[] args)")); } @@ -4831,13 +5487,15 @@ static void Main(string[] args) public async Task TestCref2() { await TestAsync( -@"class Program -{ - /// - static void Main(string[] args) - { - } -}", + """ + class Program + { + /// + static void Main(string[] args) + { + } + } + """, MainDescription(@"void Program.Main(string[] args)")); } @@ -4845,79 +5503,89 @@ static void Main(string[] args) public async Task TestCref3() { await TestAsync( -@"class Program -{ - /// - static void Main(string[] args) - { - } -}"); + """ + class Program + { + /// + static void Main(string[] args) + { + } + } + """); } [Fact] public async Task TestCref4() { await TestAsync( -@"class Program -{ - /// - static void Main(string[] args) - { - } -}"); + """ + class Program + { + /// + static void Main(string[] args) + { + } + } + """); } [Fact] public async Task TestCref5() { await TestAsync( -@"class Program -{ - /// - static void Main(string[] args) - { - } -}"); + """ + class Program + { + /// + static void Main(string[] args) + { + } + } + """); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546849")] public async Task TestIndexedProperty() { - var markup = @"class Program -{ - void M() - { - CCC c = new CCC(); - c.Index$$Prop[0] = ""s""; - } -}"; + var markup = """ + class Program + { + void M() + { + CCC c = new CCC(); + c.Index$$Prop[0] = "s"; + } + } + """; // Note that is required by compiler. Bug 17013 tracks enabling indexed property for non-COM types. - var referencedCode = @"Imports System.Runtime.InteropServices - - -Public Class CCC - -#Region ""COM GUIDs"" - Public Const ClassId As String = ""9d965fd2-1514-44f6-accd-257ce77c46b0"" - Public Const InterfaceId As String = ""a9415060-fdf0-47e3-bc80-9c18f7f39cf6"" - Public Const EventsId As String = ""c6a866a5-5f97-4b53-a5df-3739dc8ff1bb"" -# End Region - - ''' - ''' An index property from VB - ''' - ''' p1 is an integer index - ''' A string - Public Property IndexProp(ByVal p1 As Integer, Optional ByVal p2 As Integer = 0) As String - Get - Return Nothing - End Get - Set(ByVal value As String) - - End Set - End Property -End Class"; + var referencedCode = """ + Imports System.Runtime.InteropServices + + + Public Class CCC + + #Region "COM GUIDs" + Public Const ClassId As String = "9d965fd2-1514-44f6-accd-257ce77c46b0" + Public Const InterfaceId As String = "a9415060-fdf0-47e3-bc80-9c18f7f39cf6" + Public Const EventsId As String = "c6a866a5-5f97-4b53-a5df-3739dc8ff1bb" + # End Region + + ''' + ''' An index property from VB + ''' + ''' p1 is an integer index + ''' A string + Public Property IndexProp(ByVal p1 As Integer, Optional ByVal p2 As Integer = 0) As String + Get + Return Nothing + End Get + Set(ByVal value As String) + + End Set + End Property + End Class + """; await TestWithReferenceAsync(sourceCode: markup, referencedCode: referencedCode, @@ -4930,20 +5598,22 @@ await TestWithReferenceAsync(sourceCode: markup, public async Task TestUnconstructedGeneric() { await TestAsync( -@"class A -{ - enum SortOrder - { - Ascending, - Descending, - None - } + """ + class A + { + enum SortOrder + { + Ascending, + Descending, + None + } - void Goo() - { - var b = $$SortOrder.Ascending; - } -}", + void Goo() + { + var b = $$SortOrder.Ascending; + } + } + """, MainDescription(@"enum A.SortOrder")); } @@ -4951,24 +5621,28 @@ void Goo() public async Task TestUnconstructedGenericInCRef() { await TestAsync( -@"/// -class C -{ -}", + """ + /// + class C + { + } + """, MainDescription(@"class C")); } [Fact] public async Task TestAwaitableMethod() { - var markup = @"using System.Threading.Tasks; -class C -{ - async Task Goo() - { - Go$$o(); - } -}"; + var markup = """ + using System.Threading.Tasks; + class C + { + async Task Goo() + { + Go$$o(); + } + } + """; var description = $"({CSharpFeaturesResources.awaitable}) Task C.Goo()"; await VerifyWithMscorlib45Async(markup, MainDescription(description)); @@ -4977,37 +5651,41 @@ async Task Goo() [Fact] public async Task ObsoleteItem() { - var markup = @" -using System; + var markup = """ -class Program -{ - [Obsolete] - public void goo() - { - go$$o(); - } -}"; + using System; + + class Program + { + [Obsolete] + public void goo() + { + go$$o(); + } + } + """; await TestAsync(markup, MainDescription($"[{CSharpFeaturesResources.deprecated}] void Program.goo()")); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/751070")] public async Task DynamicOperator() { - var markup = @" + var markup = """ -public class Test -{ - public delegate void NoParam(); - static int Main() - { - dynamic x = new object(); - if (((System.Func)(() => (x =$$= null)))()) - return 0; - return 1; - } -}"; + public class Test + { + public delegate void NoParam(); + + static int Main() + { + dynamic x = new object(); + if (((System.Func)(() => (x =$$= null)))()) + return 0; + return 1; + } + } + """; await TestAsync(markup, MainDescription("dynamic dynamic.operator ==(dynamic left, dynamic right)")); } @@ -5015,154 +5693,180 @@ static int Main() public async Task TextOnlyDocComment() { await TestAsync( -@"/// -///goo -/// -class C$$ -{ -}", Documentation("goo")); + """ + /// + ///goo + /// + class C$$ + { + } + """, Documentation("goo")); } [Fact] public async Task TestTrimConcatMultiLine() { await TestAsync( -@"/// -/// goo -/// bar -/// -class C$$ -{ -}", Documentation("goo bar")); + """ + /// + /// goo + /// bar + /// + class C$$ + { + } + """, Documentation("goo bar")); } [Fact] public async Task TestCref() { await TestAsync( -@"/// -/// -/// -/// -class C$$ -{ -}", Documentation("C C")); + """ + /// + /// + /// + /// + class C$$ + { + } + """, Documentation("C C")); } [Fact] public async Task ExcludeTextOutsideSummaryBlock() { await TestAsync( -@"/// red -/// -/// green -/// -/// yellow -class C$$ -{ -}", Documentation("green")); + """ + /// red + /// + /// green + /// + /// yellow + class C$$ + { + } + """, Documentation("green")); } [Fact] public async Task NewlineAfterPara() { await TestAsync( -@"/// -/// goo -/// -class C$$ -{ -}", Documentation("goo")); + """ + /// + /// goo + /// + class C$$ + { + } + """, Documentation("goo")); } [Fact] public async Task TextOnlyDocComment_Metadata() { - var referenced = @" -/// -///goo -/// -public class C -{ -}"; + var referenced = """ - var code = @" -class G -{ - void goo() - { - C$$ c; - } -}"; + /// + ///goo + /// + public class C + { + } + """; + + var code = """ + + class G + { + void goo() + { + C$$ c; + } + } + """; await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", Documentation("goo")); } [Fact] public async Task TestTrimConcatMultiLine_Metadata() { - var referenced = @" -/// -/// goo -/// bar -/// -public class C -{ -}"; + var referenced = """ - var code = @" -class G -{ - void goo() - { - C$$ c; - } -}"; + /// + /// goo + /// bar + /// + public class C + { + } + """; + + var code = """ + + class G + { + void goo() + { + C$$ c; + } + } + """; await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", Documentation("goo bar")); } [Fact] public async Task TestCref_Metadata() { - var code = @" -class G -{ - void goo() - { - C$$ c; - } -}"; + var code = """ - var referenced = @"/// -/// -/// -/// -public class C -{ -}"; + class G + { + void goo() + { + C$$ c; + } + } + """; + + var referenced = """ + /// + /// + /// + /// + public class C + { + } + """; await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", Documentation("C C")); } [Fact] public async Task ExcludeTextOutsideSummaryBlock_Metadata() { - var code = @" -class G -{ - void goo() - { - C$$ c; - } -}"; + var code = """ - var referenced = @" -/// red -/// -/// green -/// -/// yellow -public class C -{ -}"; + class G + { + void goo() + { + C$$ c; + } + } + """; + + var referenced = """ + + /// red + /// + /// green + /// + /// yellow + public class C + { + } + """; await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", Documentation("green")); } @@ -5170,41 +5874,47 @@ public class C public async Task Param() { await TestAsync( -@"/// -public class C -{ - /// A type parameter of - /// First parameter of - /// Another parameter of - public void Goo(string[] arg$$s, T otherParam) - { - } -}", Documentation("First parameter of C.Goo(string[], T)")); + """ + /// + public class C + { + /// A type parameter of + /// First parameter of + /// Another parameter of + public void Goo(string[] arg$$s, T otherParam) + { + } + } + """, Documentation("First parameter of C.Goo(string[], T)")); } [Fact] public async Task Param_Metadata() { - var code = @" -class G -{ - void goo() - { - C c; - c.Goo(arg$$s: new string[] { }, 1); - } -}"; - var referenced = @" -/// -public class C -{ - /// A type parameter of - /// First parameter of - /// Another parameter of - public void Goo(string[] args, T otherParam) - { - } -}"; + var code = """ + + class G + { + void goo() + { + C c; + c.Goo(arg$$s: new string[] { }, 1); + } + } + """; + var referenced = """ + + /// + public class C + { + /// A type parameter of + /// First parameter of + /// Another parameter of + public void Goo(string[] args, T otherParam) + { + } + } + """; await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", Documentation("First parameter of C.Goo(string[], T)")); } @@ -5212,215 +5922,247 @@ public void Goo(string[] args, T otherParam) public async Task Param2() { await TestAsync( -@"/// -public class C -{ - /// A type parameter of - /// First parameter of - /// Another parameter of - public void Goo(string[] args, T oth$$erParam) - { - } -}", Documentation("Another parameter of C.Goo(string[], T)")); + """ + /// + public class C + { + /// A type parameter of + /// First parameter of + /// Another parameter of + public void Goo(string[] args, T oth$$erParam) + { + } + } + """, Documentation("Another parameter of C.Goo(string[], T)")); } [Fact] public async Task Param2_Metadata() { - var code = @" -class G -{ - void goo() - { - C c; - c.Goo(args: new string[] { }, other$$Param: 1); - } -}"; - var referenced = @" -/// -public class C -{ - /// A type parameter of - /// First parameter of - /// Another parameter of - public void Goo(string[] args, T otherParam) - { - } -}"; - await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", Documentation("Another parameter of C.Goo(string[], T)")); - } + var code = """ - [Fact] - public async Task TypeParam() - { - await TestAsync( -@"/// -public class C -{ - /// A type parameter of - /// First parameter of - /// Another parameter of - public void Goo(string[] args, T otherParam) - { - } -}", Documentation("A type parameter of C.Goo(string[], T)")); + class G + { + void goo() + { + C c; + c.Goo(args: new string[] { }, other$$Param: 1); + } + } + """; + var referenced = """ + + /// + public class C + { + /// A type parameter of + /// First parameter of + /// Another parameter of + public void Goo(string[] args, T otherParam) + { + } + } + """; + await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", Documentation("Another parameter of C.Goo(string[], T)")); } [Fact] - public async Task UnboundCref() + public async Task TypeParam() { await TestAsync( -@"/// -public class C -{ - /// A type parameter of - /// First parameter of - /// Another parameter of - public void Goo(string[] args, T otherParam) - { + """ + /// + public class C + { + /// A type parameter of + /// First parameter of + /// Another parameter of + public void Goo(string[] args, T otherParam) + { + } + } + """, Documentation("A type parameter of C.Goo(string[], T)")); } -}", Documentation("A type parameter of goo(string[], T)")); + + [Fact] + public async Task UnboundCref() + { + await TestAsync( + """ + /// + public class C + { + /// A type parameter of + /// First parameter of + /// Another parameter of + public void Goo(string[] args, T otherParam) + { + } + } + """, Documentation("A type parameter of goo(string[], T)")); } [Fact] public async Task CrefInConstructor() { await TestAsync( -@"public class TestClass -{ - /// - /// This sample shows how to specify the constructor as a cref attribute. - /// - public TestClass$$() - { - } -}", Documentation("This sample shows how to specify the TestClass constructor as a cref attribute.")); + """ + public class TestClass + { + /// + /// This sample shows how to specify the constructor as a cref attribute. + /// + public TestClass$$() + { + } + } + """, Documentation("This sample shows how to specify the TestClass constructor as a cref attribute.")); } [Fact] public async Task CrefInConstructorOverloaded() { await TestAsync( -@"public class TestClass -{ - /// - /// This sample shows how to specify the constructor as a cref attribute. - /// - public TestClass() - { - } + """ + public class TestClass + { + /// + /// This sample shows how to specify the constructor as a cref attribute. + /// + public TestClass() + { + } - /// - /// This sample shows how to specify the constructor as a cref attribute. - /// - public TestC$$lass(int value) - { - } -}", Documentation("This sample shows how to specify the TestClass(int) constructor as a cref attribute.")); + /// + /// This sample shows how to specify the constructor as a cref attribute. + /// + public TestC$$lass(int value) + { + } + } + """, Documentation("This sample shows how to specify the TestClass(int) constructor as a cref attribute.")); } [Fact] public async Task CrefInGenericMethod1() { await TestAsync( -@"public class TestClass -{ - /// - /// The GetGenericValue method. - /// This sample shows how to specify the method as a cref attribute. - /// - public static T GetGenericVa$$lue(T para) - { - return para; - } -}", Documentation("The GetGenericValue method.\r\n\r\nThis sample shows how to specify the TestClass.GetGenericValue(T) method as a cref attribute.")); + """ + public class TestClass + { + /// + /// The GetGenericValue method. + /// This sample shows how to specify the method as a cref attribute. + /// + public static T GetGenericVa$$lue(T para) + { + return para; + } + } + """, Documentation(""" + The GetGenericValue method. + + This sample shows how to specify the TestClass.GetGenericValue(T) method as a cref attribute. + """)); } [Fact] public async Task CrefInGenericMethod2() { await TestAsync( -@"public class TestClass -{ - /// - /// The GetGenericValue method. - /// This sample shows how to specify the method as a cref attribute. - /// - public static T GetGenericVa$$lue(T para) - { - return para; - } -}", Documentation("The GetGenericValue method.\r\n\r\nThis sample shows how to specify the TestClass.GetGenericValue(T) method as a cref attribute.")); + """ + public class TestClass + { + /// + /// The GetGenericValue method. + /// This sample shows how to specify the method as a cref attribute. + /// + public static T GetGenericVa$$lue(T para) + { + return para; + } + } + """, Documentation(""" + The GetGenericValue method. + + This sample shows how to specify the TestClass.GetGenericValue(T) method as a cref attribute. + """)); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/813350")] public async Task CrefInMethodOverloading1() { await TestAsync( -@"public class TestClass -{ - public static int GetZero() - { - GetGenericValu$$e(); - GetGenericValue(5); - } + """ + public class TestClass + { + public static int GetZero() + { + GetGenericValu$$e(); + GetGenericValue(5); + } - /// - /// This sample shows how to call the method - /// - public static T GetGenericValue(T para) - { - return para; - } + /// + /// This sample shows how to call the method + /// + public static T GetGenericValue(T para) + { + return para; + } - /// - /// This sample shows how to specify the method as a cref attribute. - /// - public static void GetGenericValue() - { - } -}", Documentation("This sample shows how to specify the TestClass.GetGenericValue() method as a cref attribute.")); + /// + /// This sample shows how to specify the method as a cref attribute. + /// + public static void GetGenericValue() + { + } + } + """, Documentation("This sample shows how to specify the TestClass.GetGenericValue() method as a cref attribute.")); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/813350")] public async Task CrefInMethodOverloading2() { await TestAsync( -@"public class TestClass -{ - public static int GetZero() - { - GetGenericValue(); - GetGenericVal$$ue(5); - } + """ + public class TestClass + { + public static int GetZero() + { + GetGenericValue(); + GetGenericVal$$ue(5); + } - /// - /// This sample shows how to call the method - /// - public static T GetGenericValue(T para) - { - return para; - } + /// + /// This sample shows how to call the method + /// + public static T GetGenericValue(T para) + { + return para; + } - /// - /// This sample shows how to specify the method as a cref attribute. - /// - public static void GetGenericValue() - { - } -}", Documentation("This sample shows how to call the TestClass.GetGenericValue(T) method")); + /// + /// This sample shows how to specify the method as a cref attribute. + /// + public static void GetGenericValue() + { + } + } + """, Documentation("This sample shows how to call the TestClass.GetGenericValue(T) method")); } [Fact] public async Task CrefInGenericType() { await TestAsync( -@"/// -/// This example shows how to specify the cref. -/// -class Generic$$Class -{ -}", + """ + /// + /// This example shows how to specify the cref. + /// + class Generic$$Class + { + } + """, Documentation("This example shows how to specify the GenericClass cref.", ExpectedClassifications( Text("This example shows how to specify the"), @@ -5436,26 +6178,30 @@ class Generic$$Class [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/812720")] public async Task ClassificationOfCrefsFromMetadata() { - var code = @" -class G -{ - void goo() - { - C c; - c.Go$$o(); - } -}"; - var referenced = @" -/// -public class C -{ - /// - /// See method - /// - public void Goo() - { - } -}"; + var code = """ + + class G + { + void goo() + { + C c; + c.Go$$o(); + } + } + """; + var referenced = """ + + /// + public class C + { + /// + /// See method + /// + public void Goo() + { + } + } + """; await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", Documentation("See C.Goo() method", ExpectedClassifications( @@ -5473,24 +6219,26 @@ await TestWithMetadataReferenceHelperAsync(code, referenced, "C#", "C#", [Fact] public async Task FieldAvailableInBothLinkedFiles() { - var markup = @" - - - - - - - -"; + var markup = """ + + + + + + + + + + """; await VerifyWithReferenceWorkerAsync(markup, new[] { MainDescription($"({FeaturesResources.field}) int C.x"), Usage("") }); } @@ -5498,27 +6246,35 @@ void goo() [Fact] public async Task FieldUnavailableInOneLinkedFile() { - var markup = @" - - - - - - - -"; - var expectedDescription = Usage($"\r\n{string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)}\r\n{string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Not_Available)}\r\n\r\n{FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts}", expectsWarningGlyph: true); + var markup = """ + + + + + + + + + + """; + var expectedDescription = Usage($""" + + {string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)} + {string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Not_Available)} + + {FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts} + """, expectsWarningGlyph: true); await VerifyWithReferenceWorkerAsync(markup, new[] { expectedDescription }); } @@ -5526,27 +6282,35 @@ void goo() [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37097")] public async Task BindSymbolInOtherFile() { - var markup = @" - - - - - - - -"; - var expectedDescription = Usage($"\r\n{string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Not_Available)}\r\n{string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Available)}\r\n\r\n{FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts}", expectsWarningGlyph: true); + var markup = """ + + + + + + + + + + """; + var expectedDescription = Usage($""" + + {string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Not_Available)} + {string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Available)} + + {FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts} + """, expectsWarningGlyph: true); await VerifyWithReferenceWorkerAsync(markup, new[] { expectedDescription }); } @@ -5554,31 +6318,40 @@ void goo() [Fact] public async Task FieldUnavailableInTwoLinkedFiles() { - var markup = @" - - - - - - - - - - -"; + var markup = """ + + + + + + + + + + + + + """; var expectedDescription = Usage( - $"\r\n{string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)}\r\n{string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Not_Available)}\r\n{string.Format(FeaturesResources._0_1, "Proj3", FeaturesResources.Not_Available)}\r\n\r\n{FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts}", + $""" + + {string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)} + {string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Not_Available)} + {string.Format(FeaturesResources._0_1, "Proj3", FeaturesResources.Not_Available)} + + {FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts} + """, expectsWarningGlyph: true); await VerifyWithReferenceWorkerAsync(markup, new[] { expectedDescription }); @@ -5587,83 +6360,95 @@ void goo() [Fact] public async Task ExcludeFilesWithInactiveRegions() { - var markup = @" - - + + - - - - - - - - -"; - var expectedDescription = Usage($"\r\n{string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)}\r\n{string.Format(FeaturesResources._0_1, "Proj3", FeaturesResources.Not_Available)}\r\n\r\n{FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts}", expectsWarningGlyph: true); + #if BAR + void goo() + { + x$$ + } + #endif + } + ]]> + + + + + + + + + + """; + var expectedDescription = Usage($""" + + {string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)} + {string.Format(FeaturesResources._0_1, "Proj3", FeaturesResources.Not_Available)} + + {FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts} + """, expectsWarningGlyph: true); await VerifyWithReferenceWorkerAsync(markup, new[] { expectedDescription }); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/962353")] public async Task NoValidSymbolsInLinkedDocuments() { - var markup = @" - - - - - - - -"; + var markup = """ + + + + + + + + + + """; await VerifyWithReferenceWorkerAsync(markup); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1020944")] public async Task LocalsValidInLinkedDocuments() { - var markup = @" - - - - - - - -"; + var markup = """ + + + + + + + + + + """; await VerifyWithReferenceWorkerAsync(markup, new[] { MainDescription($"({FeaturesResources.local_variable}) int x"), Usage("") }); } @@ -5671,51 +6456,61 @@ void M() [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1020944")] public async Task LocalWarningInLinkedDocuments() { - var markup = @" - - + + - - - - - -"; + int y = x$$; + } + } + ]]> + + + + + + + """; + + await VerifyWithReferenceWorkerAsync(markup, new[] { MainDescription($"({FeaturesResources.local_variable}) int x"), Usage($""" - await VerifyWithReferenceWorkerAsync(markup, new[] { MainDescription($"({FeaturesResources.local_variable}) int x"), Usage($"\r\n{string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)}\r\n{string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Not_Available)}\r\n\r\n{FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts}", expectsWarningGlyph: true) }); + {string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)} + {string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Not_Available)} + + {FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts} + """, expectsWarningGlyph: true) }); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1020944")] public async Task LabelsValidInLinkedDocuments() { - var markup = @" - - - - - - - -"; + var markup = """ + + + + + + + + + + """; await VerifyWithReferenceWorkerAsync(markup, new[] { MainDescription($"({FeaturesResources.label}) LABEL"), Usage("") }); } @@ -5723,24 +6518,26 @@ void M() [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1020944")] public async Task RangeVariablesValidInLinkedDocuments() { - var markup = @" - - - - - - - -"; + var markup = """ + + + + + + + + + + """; await VerifyWithReferenceWorkerAsync(markup, new[] { MainDescription($"({FeaturesResources.range_variable}) int y"), Usage("") }); } @@ -5748,76 +6545,84 @@ void M() [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1019766")] public async Task PointerAccessibility() { - var markup = @"class C -{ - unsafe static void Main() - { - void* p = null; - void* q = null; - dynamic d = true; - var x = p =$$= q == d; - } -}"; + var markup = """ + class C + { + unsafe static void Main() + { + void* p = null; + void* q = null; + dynamic d = true; + var x = p =$$= q == d; + } + } + """; await TestAsync(markup, MainDescription("bool void*.operator ==(void* left, void* right)")); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1114300")] public async Task AwaitingTaskOfArrayType() { - var markup = @" -using System.Threading.Tasks; + var markup = """ -class Program -{ - async Task M() - { - awa$$it M(); - } -}"; + using System.Threading.Tasks; + + class Program + { + async Task M() + { + awa$$it M(); + } + } + """; await TestAsync(markup, MainDescription(string.Format(FeaturesResources.Awaited_task_returns_0, "int[]"))); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1114300")] public async Task AwaitingTaskOfDynamic() { - var markup = @" -using System.Threading.Tasks; + var markup = """ -class Program -{ - async Task M() - { - awa$$it M(); - } -}"; + using System.Threading.Tasks; + + class Program + { + async Task M() + { + awa$$it M(); + } + } + """; await TestAsync(markup, MainDescription(string.Format(FeaturesResources.Awaited_task_returns_0, "dynamic"))); } [Fact, Trait(Traits.Feature, Traits.Features.Completion)] public async Task MethodOverloadDifferencesIgnored() { - var markup = @" - - + + - - - - -"; + }]]> + + + + + + """; var expectedDescription = $"void C.Do(int x)"; await VerifyWithReferenceWorkerAsync(markup, MainDescription(expectedDescription)); @@ -5826,51 +6631,53 @@ void Shared() [Fact, Trait(Traits.Feature, Traits.Features.Completion)] public async Task MethodOverloadDifferencesIgnored_ContainingType() { - var markup = @" - - + + - - - - - -"; + #if TWO + public class Methods2 + { + public void Do(string x) { } + } + #endif + ]]> + + + + + + + """; var expectedDescription = $"void Methods1.Do(string x)"; await VerifyWithReferenceWorkerAsync(markup, MainDescription(expectedDescription)); @@ -5880,408 +6687,507 @@ public void Do(string x) { } public async Task QuickInfoExceptions() { await TestAsync( -@"using System; + """ + using System; -namespace MyNs -{ - class MyException1 : Exception - { - } + namespace MyNs + { + class MyException1 : Exception + { + } - class MyException2 : Exception - { - } + class MyException2 : Exception + { + } - class TestClass - { - /// - /// - /// - /// - /// - void M() - { - M$$(); - } - } -}", - Exceptions($"\r\n{WorkspacesResources.Exceptions_colon}\r\n MyException1\r\n MyException2\r\n int\r\n double\r\n Not_A_Class_But_Still_Displayed")); + class TestClass + { + /// + /// + /// + /// + /// + void M() + { + M$$(); + } + } + } + """, + Exceptions($""" + + {WorkspacesResources.Exceptions_colon} + MyException1 + MyException2 + int + double + Not_A_Class_But_Still_Displayed + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnLocalFunction() { - await TestAsync(@" -class C -{ - void M() - { - int i; - local$$(); + await TestAsync(""" - void local() { i++; this.M(); } - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this, i")); + class C + { + void M() + { + int i; + local$$(); + + void local() { i++; this.M(); } + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this, i + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnLocalFunction2() { - await TestAsync(@" -class C -{ - void M() - { - int i; - local$$(i); + await TestAsync(""" - void local(int j) { j++; M(); } - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this")); + class C + { + void M() + { + int i; + local$$(i); + + void local(int j) { j++; M(); } + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnLocalFunction3() { - await TestAsync(@" -class C -{ - public void M(int @this) - { - int i = 0; - local$$(); + await TestAsync(""" - void local() - { - M(1); - i++; - @this++; - } - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this, @this, i")); + class C + { + public void M(int @this) + { + int i = 0; + local$$(); + + void local() + { + M(1); + i++; + @this++; + } + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this, @this, i + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26101")] public async Task QuickInfoCapturesOnLocalFunction4() { - await TestAsync(@" -class C -{ - int field; - void M() - { - void OuterLocalFunction$$() - { - int local = 0; - int InnerLocalFunction() + await TestAsync(""" + + class C { - field++; - return local; + int field; + void M() + { + void OuterLocalFunction$$() + { + int local = 0; + int InnerLocalFunction() + { + field++; + return local; + } + } + } } - } - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this")); + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26101")] public async Task QuickInfoCapturesOnLocalFunction5() { - await TestAsync(@" -class C -{ - int field; - void M() - { - void OuterLocalFunction() - { - int local = 0; - int InnerLocalFunction$$() + await TestAsync(""" + + class C { - field++; - return local; + int field; + void M() + { + void OuterLocalFunction() + { + int local = 0; + int InnerLocalFunction$$() + { + field++; + return local; + } + } + } } - } - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this, local")); + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this, local + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26101")] public async Task QuickInfoCapturesOnLocalFunction6() { - await TestAsync(@" -class C -{ - int field; - void M() - { - int local1 = 0; - int local2 = 0; + await TestAsync(""" - void OuterLocalFunction$$() - { - _ = local1; - void InnerLocalFunction() + class C { - _ = local2; + int field; + void M() + { + int local1 = 0; + int local2 = 0; + + void OuterLocalFunction$$() + { + _ = local1; + void InnerLocalFunction() + { + _ = local2; + } + } + } } - } - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} local1, local2")); + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} local1, local2 + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26101")] public async Task QuickInfoCapturesOnLocalFunction7() { - await TestAsync(@" -class C -{ - int field; - void M() - { - int local1 = 0; - int local2 = 0; + await TestAsync(""" - void OuterLocalFunction() - { - _ = local1; - void InnerLocalFunction$$() + class C { - _ = local2; + int field; + void M() + { + int local1 = 0; + int local2 = 0; + + void OuterLocalFunction() + { + _ = local1; + void InnerLocalFunction$$() + { + _ = local2; + } + } + } } - } - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} local2")); + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} local2 + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnLambda() { - await TestAsync(@" -class C -{ - void M() - { - int i; - System.Action a = () =$$> { i++; M(); }; - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this, i")); + await TestAsync(""" + + class C + { + void M() + { + int i; + System.Action a = () =$$> { i++; M(); }; + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this, i + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnLambda2() { - await TestAsync(@" -class C -{ - void M() - { - int i; - System.Action a = j =$$> { i++; j++; M(); }; - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this, i")); + await TestAsync(""" + + class C + { + void M() + { + int i; + System.Action a = j =$$> { i++; j++; M(); }; + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this, i + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnLambda2_DifferentOrder() { - await TestAsync(@" -class C -{ - void M(int j) - { - int i; - System.Action a = () =$$> { M(); i++; j++; }; - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this, j, i")); + await TestAsync(""" + + class C + { + void M(int j) + { + int i; + System.Action a = () =$$> { M(); i++; j++; }; + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this, j, i + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnLambda3() { - await TestAsync(@" -class C -{ - void M() - { - int i; - int @this; - N(() =$$> { M(); @this++; }, () => { i++; }); - } - void N(System.Action x, System.Action y) { } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this, @this")); + await TestAsync(""" + + class C + { + void M() + { + int i; + int @this; + N(() =$$> { M(); @this++; }, () => { i++; }); + } + void N(System.Action x, System.Action y) { } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this, @this + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnLambda4() { - await TestAsync(@" -class C -{ - void M() - { - int i; - N(() => { M(); }, () =$$> { i++; }); - } - void N(System.Action x, System.Action y) { } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} i")); + await TestAsync(""" + + class C + { + void M() + { + int i; + N(() => { M(); }, () =$$> { i++; }); + } + void N(System.Action x, System.Action y) { } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} i + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26101")] public async Task QuickInfoCapturesOnLambda5() { - await TestAsync(@" -class C -{ - int field; - void M() - { - System.Action a = () =$$> - { - int local = 0; - System.Func b = () => + await TestAsync(""" + + class C { - field++; - return local; - }; - }; - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this")); + int field; + void M() + { + System.Action a = () =$$> + { + int local = 0; + System.Func b = () => + { + field++; + return local; + }; + }; + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26101")] public async Task QuickInfoCapturesOnLambda6() { - await TestAsync(@" -class C -{ - int field; - void M() - { - System.Action a = () => - { - int local = 0; - System.Func b = () =$$> + await TestAsync(""" + + class C { - field++; - return local; - }; - }; - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} this, local")); + int field; + void M() + { + System.Action a = () => + { + int local = 0; + System.Func b = () =$$> + { + field++; + return local; + }; + }; + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} this, local + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26101")] public async Task QuickInfoCapturesOnLambda7() { - await TestAsync(@" -class C -{ - int field; - void M() - { - int local1 = 0; - int local2 = 0; + await TestAsync(""" - System.Action a = () =$$> - { - _ = local1; - System.Action b = () => + class C { - _ = local2; - }; - }; - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} local1, local2")); + int field; + void M() + { + int local1 = 0; + int local2 = 0; + + System.Action a = () =$$> + { + _ = local1; + System.Action b = () => + { + _ = local2; + }; + }; + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} local1, local2 + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26101")] public async Task QuickInfoCapturesOnLambda8() { - await TestAsync(@" -class C -{ - int field; - void M() - { - int local1 = 0; - int local2 = 0; + await TestAsync(""" - System.Action a = () => - { - _ = local1; - System.Action b = () =$$> + class C { - _ = local2; - }; - }; - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} local2")); + int field; + void M() + { + int local1 = 0; + int local2 = 0; + + System.Action a = () => + { + _ = local1; + System.Action b = () =$$> + { + _ = local2; + }; + }; + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} local2 + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23307")] public async Task QuickInfoCapturesOnDelegate() { - await TestAsync(@" -class C -{ - void M() - { - int i; - System.Func f = dele$$gate(bool b) { i++; return 1; }; - } -}", - Captures($"\r\n{WorkspacesResources.Variables_captured_colon} i")); + await TestAsync(""" + + class C + { + void M() + { + int i; + System.Func f = dele$$gate(bool b) { i++; return 1; }; + } + } + """, + Captures($""" + + {WorkspacesResources.Variables_captured_colon} i + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1516")] public async Task QuickInfoWithNonStandardSeeAttributesAppear() { await TestAsync( -@"class C -{ - /// - /// - /// - /// - /// - /// - void M() - { - M$$(); - } -}", + """ + class C + { + /// + /// + /// + /// + /// + /// + void M() + { + M$$(); + } + } + """, Documentation(@"string http://microsoft.com null cat")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/6657")] public async Task OptionalParameterFromPreviousSubmission() { - const string workspaceDefinition = @" - - - void M(int x = 1) { } - - - M(x$$: 2) - - -"; + const string workspaceDefinition = """ + + + + void M(int x = 1) { } + + + M(x$$: 2) + + + + """; using var workspace = EditorTestWorkspace.Create(XElement.Parse(workspaceDefinition), workspaceKind: WorkspaceKind.Interactive); await TestWithOptionsAsync(workspace, MainDescription($"({FeaturesResources.parameter}) int x = 1")); } @@ -6290,25 +7196,27 @@ void M(int x = 1) { } public async Task TupleProperty() { await TestInMethodAsync( -@"interface I -{ - (int, int) Name { get; set; } -} + """ + interface I + { + (int, int) Name { get; set; } + } -class C : I -{ - (int, int) I.Name$$ - { - get - { - throw new System.Exception(); - } + class C : I + { + (int, int) I.Name$$ + { + get + { + throw new System.Exception(); + } - set - { - } - } -}", + set + { + } + } + } + """, MainDescription("(int, int) C.Name { get; set; }")); } @@ -6316,16 +7224,18 @@ class C : I public async Task ValueTupleWithArity0VariableName() { await TestAsync( -@" -using System; -public class C -{ - void M() - { - var y$$ = ValueTuple.Create(); - } -} -", + """ + + using System; + public class C + { + void M() + { + var y$$ = ValueTuple.Create(); + } + } + + """, MainDescription($"({FeaturesResources.local_variable}) ValueTuple y")); } @@ -6333,16 +7243,18 @@ void M() public async Task ValueTupleWithArity0ImplicitVar() { await TestAsync( -@" -using System; -public class C -{ - void M() - { - var$$ y = ValueTuple.Create(); - } -} -", + """ + + using System; + public class C + { + void M() + { + var$$ y = ValueTuple.Create(); + } + } + + """, MainDescription("struct System.ValueTuple")); } @@ -6350,16 +7262,18 @@ void M() public async Task ValueTupleWithArity1VariableName() { await TestAsync( -@" -using System; -public class C -{ - void M() - { - var y$$ = ValueTuple.Create(1); - } -} -", + """ + + using System; + public class C + { + void M() + { + var y$$ = ValueTuple.Create(1); + } + } + + """, MainDescription($"({FeaturesResources.local_variable}) ValueTuple y")); } @@ -6367,16 +7281,18 @@ void M() public async Task ValueTupleWithArity1ImplicitVar() { await TestAsync( -@" -using System; -public class C -{ - void M() - { - var$$ y = ValueTuple.Create(1); - } -} -", + """ + + using System; + public class C + { + void M() + { + var$$ y = ValueTuple.Create(1); + } + } + + """, MainDescription("struct System.ValueTuple")); } @@ -6384,16 +7300,18 @@ void M() public async Task ValueTupleWithArity2VariableName() { await TestAsync( -@" -using System; -public class C -{ - void M() - { - var y$$ = ValueTuple.Create(1, 1); - } -} -", + """ + + using System; + public class C + { + void M() + { + var y$$ = ValueTuple.Create(1, 1); + } + } + + """, MainDescription($"({FeaturesResources.local_variable}) (int, int) y")); } @@ -6401,16 +7319,18 @@ void M() public async Task ValueTupleWithArity2ImplicitVar() { await TestAsync( -@" -using System; -public class C -{ - void M() - { - var$$ y = ValueTuple.Create(1, 1); - } -} -", + """ + + using System; + public class C + { + void M() + { + var$$ y = ValueTuple.Create(1, 1); + } + } + + """, MainDescription("(int, int)")); } @@ -6418,20 +7338,22 @@ void M() public async Task TestRefMethod() { await TestInMethodAsync( -@"using System; + """ + using System; -class Program -{ - static void Main(string[] args) - { - ref int i = ref $$goo(); - } + class Program + { + static void Main(string[] args) + { + ref int i = ref $$goo(); + } - private static ref int goo() - { - throw new NotImplementedException(); - } -}", + private static ref int goo() + { + throw new NotImplementedException(); + } + } + """, MainDescription("ref int Program.goo()")); } @@ -6439,20 +7361,22 @@ private static ref int goo() public async Task TestRefLocal() { await TestInMethodAsync( -@"using System; + """ + using System; -class Program -{ - static void Main(string[] args) - { - ref int $$i = ref goo(); - } + class Program + { + static void Main(string[] args) + { + ref int $$i = ref goo(); + } - private static ref int goo() - { - throw new NotImplementedException(); - } -}", + private static ref int goo() + { + throw new NotImplementedException(); + } + } + """, MainDescription($"({FeaturesResources.local_variable}) ref int i")); } @@ -6460,21 +7384,23 @@ private static ref int goo() public async Task TestGenericMethodInDocComment() { await TestAsync( -@" -class Test -{ - T F() - { - F(); - } + """ - /// - /// - /// - void S() - { } -} -", + class Test + { + T F() + { + F(); + } + + /// + /// + /// + void S() + { } + } + + """, MainDescription("T Test.F()")); } @@ -6482,15 +7408,17 @@ void S() public async Task TestExceptionWithCrefToConstructorDoesNotCrash() { await TestAsync( -@" -class Test -{ - /// - /// - /// - public Test$$() {} -} -", + """ + + class Test + { + /// + /// + /// + public Test$$() {} + } + + """, MainDescription("Test.Test()")); } @@ -6504,11 +7432,13 @@ public async Task TestRefStruct() [Fact] public async Task TestRefStruct_Nested() { - var markup = @" -namespace Nested -{ - ref struct X$$ {} -}"; + var markup = """ + + namespace Nested + { + ref struct X$$ {} + } + """; await TestAsync(markup, MainDescription("ref struct Nested.X")); } @@ -6522,11 +7452,13 @@ public async Task TestReadOnlyStruct() [Fact] public async Task TestReadOnlyStruct_Nested() { - var markup = @" -namespace Nested -{ - readonly struct X$$ {} -}"; + var markup = """ + + namespace Nested + { + readonly struct X$$ {} + } + """; await TestAsync(markup, MainDescription("readonly struct Nested.X")); } @@ -6540,35 +7472,39 @@ public async Task TestReadOnlyRefStruct() [Fact] public async Task TestReadOnlyRefStruct_Nested() { - var markup = @" -namespace Nested -{ - readonly ref struct X$$ {} -}"; + var markup = """ + + namespace Nested + { + readonly ref struct X$$ {} + } + """; await TestAsync(markup, MainDescription("readonly ref struct Nested.X")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/22450")] public async Task TestRefLikeTypesNoDeprecated() { - var xmlString = @" - - - - -public ref struct TestRef -{ -} - - - -ref struct Test -{ - private $$TestRef _field; -} - - -"; + var xmlString = """ + + + + + + public ref struct TestRef + { + } + + + + ref struct Test + { + private $$TestRef _field; + } + + + + """; // There should be no [deprecated] attribute displayed. await VerifyWithReferenceWorkerAsync(xmlString, MainDescription($"ref struct TestRef")); @@ -6578,24 +7514,26 @@ ref struct Test public async Task PropertyWithSameNameAsOtherType() { await TestAsync( -@"namespace ConsoleApplication1 -{ - class Program - { - static A B { get; set; } - static B A { get; set; } + """ + namespace ConsoleApplication1 + { + class Program + { + static A B { get; set; } + static B A { get; set; } - static void Main(string[] args) - { - B = ConsoleApplication1.B$$.F(); - } - } - class A { } - class B - { - public static A F() => null; - } -}", + static void Main(string[] args) + { + B = ConsoleApplication1.B$$.F(); + } + } + class A { } + class B + { + public static A F() => null; + } + } + """, MainDescription($"ConsoleApplication1.A ConsoleApplication1.B.F()")); } @@ -6603,26 +7541,28 @@ class B public async Task PropertyWithSameNameAsOtherType2() { await TestAsync( -@"using System.Collections.Generic; + """ + using System.Collections.Generic; -namespace ConsoleApplication1 -{ - class Program - { - public static List Bar { get; set; } + namespace ConsoleApplication1 + { + class Program + { + public static List Bar { get; set; } - static void Main(string[] args) - { - Tes$$t(); - } + static void Main(string[] args) + { + Tes$$t(); + } - static void Test() { } - } + static void Test() { } + } - class Bar - { - } -}", + class Bar + { + } + } + """, MainDescription($"void Program.Test()")); } @@ -6630,36 +7570,40 @@ class Bar public async Task InMalformedEmbeddedStatement_01() { await TestAsync( -@" -class Program -{ - void method1() - { - if (method2()) - .Any(b => b.Content$$Type, out var chars) - { - } - } -} -"); + """ + + class Program + { + void method1() + { + if (method2()) + .Any(b => b.Content$$Type, out var chars) + { + } + } + } + + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23883")] public async Task InMalformedEmbeddedStatement_02() { await TestAsync( -@" -class Program -{ - void method1() - { - if (method2()) - .Any(b => b$$.ContentType, out var chars) - { - } - } -} -", + """ + + class Program + { + void method1() + { + if (method2()) + .Any(b => b$$.ContentType, out var chars) + { + } + } + } + + """, MainDescription($"({FeaturesResources.parameter}) ? b")); } @@ -6667,11 +7611,13 @@ void method1() public async Task EnumConstraint() { await TestInMethodAsync( -@" -class X where T : System.Enum -{ - private $$T x; -}", + """ + + class X where T : System.Enum + { + private $$T x; + } + """, MainDescription($"T {FeaturesResources.in_} X where T : Enum")); } @@ -6679,11 +7625,13 @@ class X where T : System.Enum public async Task DelegateConstraint() { await TestInMethodAsync( -@" -class X where T : System.Delegate -{ - private $$T x; -}", + """ + + class X where T : System.Delegate + { + private $$T x; + } + """, MainDescription($"T {FeaturesResources.in_} X where T : Delegate")); } @@ -6691,11 +7639,13 @@ class X where T : System.Delegate public async Task MulticastDelegateConstraint() { await TestInMethodAsync( -@" -class X where T : System.MulticastDelegate -{ - private $$T x; -}", + """ + + class X where T : System.MulticastDelegate + { + private $$T x; + } + """, MainDescription($"T {FeaturesResources.in_} X where T : MulticastDelegate")); } @@ -6703,10 +7653,12 @@ class X where T : System.MulticastDelegate public async Task UnmanagedConstraint_Type() { await TestAsync( -@" -class $$X where T : unmanaged -{ -}", + """ + + class $$X where T : unmanaged + { + } + """, MainDescription("class X where T : unmanaged")); } @@ -6714,11 +7666,13 @@ class $$X where T : unmanaged public async Task UnmanagedConstraint_Method() { await TestAsync( -@" -class X -{ - void $$M() where T : unmanaged { } -}", + """ + + class X + { + void $$M() where T : unmanaged { } + } + """, MainDescription("void X.M() where T : unmanaged")); } @@ -6734,14 +7688,16 @@ await TestAsync( public async Task UnmanagedConstraint_LocalFunction() { await TestAsync( -@" -class X -{ - void N() - { - void $$M() where T : unmanaged { } - } -}", + """ + + class X + { + void N() + { + void $$M() where T : unmanaged { } + } + } + """, MainDescription("void M() where T : unmanaged")); } @@ -6749,12 +7705,14 @@ void N() public async Task TestGetAccessorDocumentation() { await TestAsync( -@" -class X -{ - /// Summary for property Goo - int Goo { g$$et; set; } -}", + """ + + class X + { + /// Summary for property Goo + int Goo { g$$et; set; } + } + """, Documentation("Summary for property Goo")); } @@ -6762,12 +7720,14 @@ class X public async Task TestSetAccessorDocumentation() { await TestAsync( -@" -class X -{ - /// Summary for property Goo - int Goo { get; s$$et; } -}", + """ + + class X + { + /// Summary for property Goo + int Goo { get; s$$et; } + } + """, Documentation("Summary for property Goo")); } @@ -6775,18 +7735,20 @@ class X public async Task TestEventAddDocumentation1() { await TestAsync( -@" -using System; + """ -class X -{ - /// Summary for event Goo - event EventHandler Goo - { - a$$dd => throw null; - remove => throw null; - } -}", + using System; + + class X + { + /// Summary for event Goo + event EventHandler Goo + { + a$$dd => throw null; + remove => throw null; + } + } + """, Documentation("Summary for event Goo")); } @@ -6794,16 +7756,18 @@ event EventHandler Goo public async Task TestEventAddDocumentation2() { await TestAsync( -@" -using System; + """ -class X -{ - /// Summary for event Goo - event EventHandler Goo; + using System; + + class X + { + /// Summary for event Goo + event EventHandler Goo; - void M() => Goo +$$= null; -}", + void M() => Goo +$$= null; + } + """, Documentation("Summary for event Goo")); } @@ -6811,18 +7775,20 @@ class X public async Task TestEventRemoveDocumentation1() { await TestAsync( -@" -using System; + """ -class X -{ - /// Summary for event Goo - event EventHandler Goo - { - add => throw null; - r$$emove => throw null; - } -}", + using System; + + class X + { + /// Summary for event Goo + event EventHandler Goo + { + add => throw null; + r$$emove => throw null; + } + } + """, Documentation("Summary for event Goo")); } @@ -6830,16 +7796,18 @@ event EventHandler Goo public async Task TestEventRemoveDocumentation2() { await TestAsync( -@" -using System; + """ -class X -{ - /// Summary for event Goo - event EventHandler Goo; + using System; + + class X + { + /// Summary for event Goo + event EventHandler Goo; - void M() => Goo -$$= null; -}", + void M() => Goo -$$= null; + } + """, Documentation("Summary for event Goo")); } @@ -6847,14 +7815,16 @@ class X public async Task BuiltInOperatorWithUserDefinedEquivalent() { await TestAsync( -@" -class X -{ - void N(string a, string b) - { - var v = a $$== b; - } -}", + """ + + class X + { + void N(string a, string b) + { + var v = a $$== b; + } + } + """, MainDescription("bool string.operator ==(string a, string b)"), SymbolGlyph(Glyph.Operator)); } @@ -6863,10 +7833,12 @@ void N(string a, string b) public async Task NotNullConstraint_Type() { await TestAsync( -@" -class $$X where T : notnull -{ -}", + """ + + class $$X where T : notnull + { + } + """, MainDescription("class X where T : notnull")); } @@ -6874,14 +7846,51 @@ class $$X where T : notnull public async Task NotNullConstraint_Method() { await TestAsync( -@" -class X -{ - void $$M() where T : notnull { } -}", + """ + + class X + { + void $$M() where T : notnull { } + } + """, MainDescription("void X.M() where T : notnull")); } + [Fact] + public async Task MultipleConstraints_Type() + { + await TestAsync( + """ + + class $$X where T : notnull where U : notnull + { + } + """, + MainDescription(""" + class X + where T : notnull + where U : notnull + """)); + } + + [Fact] + public async Task MultipleConstraints_Method() + { + await TestAsync( + """ + + class X + { + void $$M() where T : notnull where U : notnull { } + } + """, + MainDescription(""" + void X.M() + where T : notnull + where U : notnull + """)); + } + [Fact] public async Task NotNullConstraint_Delegate() { @@ -6894,14 +7903,16 @@ await TestAsync( public async Task NotNullConstraint_LocalFunction() { await TestAsync( -@" -class X -{ - void N() - { - void $$M() where T : notnull { } - } -}", + """ + + class X + { + void N() + { + void $$M() where T : notnull { } + } + } + """, MainDescription("void M() where T : notnull")); } @@ -6909,15 +7920,17 @@ void N() public async Task NullableParameterThatIsMaybeNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -class X -{ - void N(string? s) - { - string s2 = $$s; - } -}", + class X + { + void N(string? s) + { + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.parameter}) string? s"), NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "s"))); } @@ -6926,16 +7939,18 @@ void N(string? s) public async Task NullableParameterThatIsNotNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -class X -{ - void N(string? s) - { - s = """"; - string s2 = $$s; - } -}", + class X + { + void N(string? s) + { + s = ""; + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.parameter}) string? s"), NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s"))); } @@ -6944,17 +7959,19 @@ void N(string? s) public async Task NullableFieldThatIsMaybeNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -class X -{ - string? s = null; + class X + { + string? s = null; - void N() - { - string s2 = $$s; - } -}", + void N() + { + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.field}) string? X.s"), NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "s"))); } @@ -6963,18 +7980,20 @@ void N() public async Task NullableFieldThatIsNotNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -class X -{ - string? s = null; + class X + { + string? s = null; - void N() - { - s = """"; - string s2 = $$s; - } -}", + void N() + { + s = ""; + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.field}) string? X.s"), NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s"))); } @@ -6983,17 +8002,19 @@ void N() public async Task NullablePropertyThatIsMaybeNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -class X -{ - string? S { get; set; } + class X + { + string? S { get; set; } - void N() - { - string s2 = $$S; - } -}", + void N() + { + string s2 = $$S; + } + } + """, MainDescription("string? X.S { get; set; }"), NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "S"))); } @@ -7002,18 +8023,20 @@ void N() public async Task NullablePropertyThatIsNotNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -class X -{ - string? S { get; set; } + class X + { + string? S { get; set; } - void N() - { - S = """"; - string s2 = $$S; - } -}", + void N() + { + S = ""; + string s2 = $$S; + } + } + """, MainDescription("string? X.S { get; set; }"), NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "S"))); } @@ -7022,22 +8045,24 @@ void N() public async Task NullableRangeVariableThatIsMaybeNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -using System.Collections.Generic; + using System.Collections.Generic; -class X -{ - void N() - { - IEnumerable enumerable; + class X + { + void N() + { + IEnumerable enumerable; - foreach (var s in enumerable) - { - string s2 = $$s; - } - } -}", + foreach (var s in enumerable) + { + string s2 = $$s; + } + } + } + """, MainDescription($"({FeaturesResources.local_variable}) string? s"), NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "s"))); } @@ -7046,22 +8071,24 @@ void N() public async Task NullableRangeVariableThatIsNotNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -using System.Collections.Generic; + using System.Collections.Generic; -class X -{ - void N() - { - IEnumerable enumerable; + class X + { + void N() + { + IEnumerable enumerable; - foreach (var s in enumerable) - { - string s2 = $$s; - } - } -}", + foreach (var s in enumerable) + { + string s2 = $$s; + } + } + } + """, MainDescription($"({FeaturesResources.local_variable}) string? s"), NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s"))); } @@ -7070,18 +8097,20 @@ void N() public async Task NullableLocalThatIsMaybeNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -using System.Collections.Generic; + using System.Collections.Generic; -class X -{ - void N() - { - string? s = null; - string s2 = $$s; - } -}", + class X + { + void N() + { + string? s = null; + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.local_variable}) string? s"), NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "s"))); } @@ -7090,18 +8119,20 @@ void N() public async Task NullableLocalThatIsNotNull() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -using System.Collections.Generic; + using System.Collections.Generic; -class X -{ - void N() - { - string? s = """"; - string s2 = $$s; - } -}", + class X + { + void N() + { + string? s = ""; + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.local_variable}) string? s"), NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s"))); } @@ -7110,18 +8141,20 @@ void N() public async Task NullableNotShownPriorToLanguageVersion8() { await TestWithOptionsAsync(TestOptions.Regular7_3, -@"#nullable enable - -using System.Collections.Generic; - -class X -{ - void N() - { - string s = """"; - string s2 = $$s; - } -}", + """ + #nullable enable + + using System.Collections.Generic; + + class X + { + void N() + { + string s = ""; + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.local_variable}) string s"), NullabilityAnalysis("")); } @@ -7130,18 +8163,20 @@ void N() public async Task NullableNotShownInNullableDisable() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable disable + """ + #nullable disable -using System.Collections.Generic; + using System.Collections.Generic; -class X -{ - void N() - { - string s = """"; - string s2 = $$s; - } -}", + class X + { + void N() + { + string s = ""; + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.local_variable}) string s"), NullabilityAnalysis("")); } @@ -7150,16 +8185,18 @@ void N() public async Task NullableShownWhenEnabledGlobally() { await TestWithOptionsAsync(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: NullableContextOptions.Enable), -@"using System.Collections.Generic; + """ + using System.Collections.Generic; -class X -{ - void N() - { - string s = """"; - string s2 = $$s; - } -}", + class X + { + void N() + { + string s = ""; + string s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.local_variable}) string s"), NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s"))); } @@ -7168,18 +8205,20 @@ void N() public async Task NullableNotShownForValueType() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -using System.Collections.Generic; + using System.Collections.Generic; -class X -{ - void N() - { - int a = 0; - int b = $$a; - } -}", + class X + { + void N() + { + int a = 0; + int b = $$a; + } + } + """, MainDescription($"({FeaturesResources.local_variable}) int a"), NullabilityAnalysis("")); } @@ -7188,18 +8227,20 @@ void N() public async Task NullableNotShownForConst() { await TestWithOptionsAsync(TestOptions.Regular8, -@"#nullable enable + """ + #nullable enable -using System.Collections.Generic; + using System.Collections.Generic; -class X -{ - void N() - { - const string? s = null; - string? s2 = $$s; - } -}", + class X + { + void N() + { + const string? s = null; + string? s2 = $$s; + } + } + """, MainDescription($"({FeaturesResources.local_constant}) string? s = null"), NullabilityAnalysis("")); } @@ -7208,13 +8249,15 @@ void N() public async Task TestInheritdocInlineSummary() { var markup = -@" -/// Summary documentation -/// Remarks documentation -void M(int x) { } + """ + + /// Summary documentation + /// Remarks documentation + void M(int x) { } -/// -void $$M(int x, int y) { }"; + /// + void $$M(int x, int y) { } + """; await TestInClassAsync(markup, MainDescription("void C.M(int x, int y)"), @@ -7225,16 +8268,18 @@ await TestInClassAsync(markup, public async Task TestInheritdocTwoLevels1() { var markup = -@" -/// Summary documentation -/// Remarks documentation -void M() { } + """ -/// -void M(int x) { } + /// Summary documentation + /// Remarks documentation + void M() { } -/// -void $$M(int x, int y) { }"; + /// + void M(int x) { } + + /// + void $$M(int x, int y) { } + """; await TestInClassAsync(markup, MainDescription("void C.M(int x, int y)"), @@ -7245,16 +8290,18 @@ await TestInClassAsync(markup, public async Task TestInheritdocTwoLevels2() { var markup = -@" -/// Summary documentation -/// Remarks documentation -void M() { } + """ + + /// Summary documentation + /// Remarks documentation + void M() { } -/// -void M(int x) { } + /// + void M(int x) { } -/// -void $$M(int x, int y) { }"; + /// + void $$M(int x, int y) { } + """; await TestInClassAsync(markup, MainDescription("void C.M(int x, int y)"), @@ -7265,26 +8312,28 @@ await TestInClassAsync(markup, public async Task TestInheritdocWithTypeParamRef() { var markup = -@" -public class Program -{ - public static void Main() => _ = new Test().$$Clone(); -} + """ -public class Test : ICloneable> -{ - /// - public Test Clone() => new(); -} + public class Program + { + public static void Main() => _ = new Test().$$Clone(); + } -/// A type that has clonable instances. -/// The type of instances that can be cloned. -public interface ICloneable -{ - /// Clones a . - /// A clone of the . - public T Clone(); -}"; + public class Test : ICloneable> + { + /// + public Test Clone() => new(); + } + + /// A type that has clonable instances. + /// The type of instances that can be cloned. + public interface ICloneable + { + /// Clones a . + /// A clone of the . + public T Clone(); + } + """; await TestInClassAsync(markup, MainDescription("Test Test.Clone()"), @@ -7295,21 +8344,23 @@ await TestInClassAsync(markup, public async Task TestInheritdocWithTypeParamRef1() { var markup = -@" -public interface ITest -{ - /// - /// A generic method . - /// - /// A generic type. - void Foo(); -} + """ -public class Test : ITest -{ - /// - public void $$Foo() { } -}"; + public interface ITest + { + /// + /// A generic method . + /// + /// A generic type. + void Foo(); + } + + public class Test : ITest + { + /// + public void $$Foo() { } + } + """; await TestWithOptionsAsync(TestOptions.Regular8, markup, @@ -7324,12 +8375,14 @@ await TestWithOptionsAsync(TestOptions.Regular8, public async Task TestInheritdocCycle1() { var markup = -@" -/// -void M(int x) { } + """ -/// -void $$M(int x, int y) { }"; + /// + void M(int x) { } + + /// + void $$M(int x, int y) { } + """; await TestInClassAsync(markup, MainDescription("void C.M(int x, int y)"), @@ -7340,9 +8393,11 @@ await TestInClassAsync(markup, public async Task TestInheritdocCycle2() { var markup = -@" -/// -void $$M(int x) { }"; + """ + + /// + void $$M(int x) { } + """; await TestInClassAsync(markup, MainDescription("void C.M(int x)"), @@ -7353,9 +8408,11 @@ await TestInClassAsync(markup, public async Task TestInheritdocCycle3() { var markup = -@" -/// -void $$M(int x) { }"; + """ + + /// + void $$M(int x) { } + """; await TestInClassAsync(markup, MainDescription("void C.M(int x)"), @@ -7366,13 +8423,15 @@ await TestInClassAsync(markup, public async Task TestLinqGroupVariableDeclaration() { var code = -@" -void M(string[] a) -{ - var v = from x in a - group x by x.Length into $$g - select g; -}"; + """ + + void M(string[] a) + { + var v = from x in a + group x by x.Length into $$g + select g; + } + """; await TestInClassAsync(code, MainDescription($"({FeaturesResources.range_variable}) IGrouping g")); @@ -7381,247 +8440,297 @@ await TestInClassAsync(code, [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38283")] public async Task QuickInfoOnIndexerCloseBracket() { - await TestAsync(@" -class C -{ - public int this[int x] { get { return 1; } } + await TestAsync(""" - void M() - { - var x = new C()[5$$]; - } -}", + class C + { + public int this[int x] { get { return 1; } } + + void M() + { + var x = new C()[5$$]; + } + } + """, MainDescription("int C.this[int x] { get; }")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38283")] public async Task QuickInfoOnIndexerOpenBracket() { - await TestAsync(@" -class C -{ - public int this[int x] { get { return 1; } } + await TestAsync(""" - void M() - { - var x = new C()$$[5]; - } -}", + class C + { + public int this[int x] { get { return 1; } } + + void M() + { + var x = new C()$$[5]; + } + } + """, MainDescription("int C.this[int x] { get; }")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38283")] public async Task QuickInfoOnIndexer_NotOnArrayAccess() { - await TestAsync(@" -class Program -{ - void M() - { - int[] x = new int[4]; - int y = x[3$$]; - } -}", + await TestAsync(""" + + class Program + { + void M() + { + int[] x = new int[4]; + int y = x[3$$]; + } + } + """, MainDescription("struct System.Int32")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31618")] public async Task QuickInfoWithRemarksOnMethod() { - await TestAsync(@" -class Program -{ - /// - /// Summary text - /// - /// - /// Remarks text - /// - int M() - { - return $$M(); - } -}", + await TestAsync(""" + + class Program + { + /// + /// Summary text + /// + /// + /// Remarks text + /// + int M() + { + return $$M(); + } + } + """, MainDescription("int Program.M()"), Documentation("Summary text"), - Remarks("\r\nRemarks text")); + Remarks(""" + + Remarks text + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31618")] public async Task QuickInfoWithRemarksOnPropertyAccessor() { - await TestAsync(@" -class Program -{ - /// - /// Summary text - /// - /// - /// Remarks text - /// - int M { $$get; } -}", + await TestAsync(""" + + class Program + { + /// + /// Summary text + /// + /// + /// Remarks text + /// + int M { $$get; } + } + """, MainDescription("int Program.M.get"), Documentation("Summary text"), - Remarks("\r\nRemarks text")); + Remarks(""" + + Remarks text + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31618")] public async Task QuickInfoWithReturnsOnMethod() { - await TestAsync(@" -class Program -{ - /// - /// Summary text - /// - /// - /// Returns text - /// - int M() - { - return $$M(); - } -}", + await TestAsync(""" + + class Program + { + /// + /// Summary text + /// + /// + /// Returns text + /// + int M() + { + return $$M(); + } + } + """, MainDescription("int Program.M()"), Documentation("Summary text"), - Returns($"\r\n{FeaturesResources.Returns_colon}\r\n Returns text")); + Returns($""" + + {FeaturesResources.Returns_colon} + Returns text + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31618")] public async Task QuickInfoWithReturnsOnPropertyAccessor() { - await TestAsync(@" -class Program -{ - /// - /// Summary text - /// - /// - /// Returns text - /// - int M { $$get; } -}", + await TestAsync(""" + + class Program + { + /// + /// Summary text + /// + /// + /// Returns text + /// + int M { $$get; } + } + """, MainDescription("int Program.M.get"), Documentation("Summary text"), - Returns($"\r\n{FeaturesResources.Returns_colon}\r\n Returns text")); + Returns($""" + + {FeaturesResources.Returns_colon} + Returns text + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31618")] public async Task QuickInfoWithValueOnMethod() { - await TestAsync(@" -class Program -{ - /// - /// Summary text - /// - /// - /// Value text - /// - int M() - { - return $$M(); - } -}", + await TestAsync(""" + + class Program + { + /// + /// Summary text + /// + /// + /// Value text + /// + int M() + { + return $$M(); + } + } + """, MainDescription("int Program.M()"), Documentation("Summary text"), - Value($"\r\n{FeaturesResources.Value_colon}\r\n Value text")); + Value($""" + + {FeaturesResources.Value_colon} + Value text + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31618")] public async Task QuickInfoWithValueOnPropertyAccessor() { - await TestAsync(@" -class Program -{ - /// - /// Summary text - /// - /// - /// Value text - /// - int M { $$get; } -}", + await TestAsync(""" + + class Program + { + /// + /// Summary text + /// + /// + /// Value text + /// + int M { $$get; } + } + """, MainDescription("int Program.M.get"), Documentation("Summary text"), - Value($"\r\n{FeaturesResources.Value_colon}\r\n Value text")); + Value($""" + + {FeaturesResources.Value_colon} + Value text + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42368")] public async Task QuickInfoNotPattern1() { - await TestAsync(@" -class Person -{ - void Goo(object o) - { - if (o is not $$Person p) - { - } - } -}", + await TestAsync(""" + + class Person + { + void Goo(object o) + { + if (o is not $$Person p) + { + } + } + } + """, MainDescription("class Person")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42368")] public async Task QuickInfoNotPattern2() { - await TestAsync(@" -class Person -{ - void Goo(object o) - { - if (o is $$not Person p) - { - } - } -}"); + await TestAsync(""" + + class Person + { + void Goo(object o) + { + if (o is $$not Person p) + { + } + } + } + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42368")] public async Task QuickInfoOrPattern1() { - await TestAsync(@" -class Person -{ - void Goo(object o) - { - if (o is $$Person or int) - { - } - } -}", MainDescription("class Person")); + await TestAsync(""" + + class Person + { + void Goo(object o) + { + if (o is $$Person or int) + { + } + } + } + """, MainDescription("class Person")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42368")] public async Task QuickInfoOrPattern2() { - await TestAsync(@" -class Person -{ - void Goo(object o) - { - if (o is Person or $$int) - { - } - } -}", MainDescription("struct System.Int32")); + await TestAsync(""" + + class Person + { + void Goo(object o) + { + if (o is Person or $$int) + { + } + } + } + """, MainDescription("struct System.Int32")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42368")] public async Task QuickInfoOrPattern3() { - await TestAsync(@" -class Person -{ - void Goo(object o) - { - if (o is Person $$or int) - { - } - } -}"); + await TestAsync(""" + + class Person + { + void Goo(object o) + { + if (o is Person $$or int) + { + } + } + } + """); } [Fact] @@ -7629,12 +8738,14 @@ public async Task QuickInfoRecord() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"record Person(string First, string Last) -{ - void M($$Person p) - { - } -}", MainDescription("record Person")); + """ + record Person(string First, string Last) + { + void M($$Person p) + { + } + } + """, MainDescription("record Person")); } [Fact] @@ -7642,105 +8753,119 @@ public async Task QuickInfoDerivedRecord() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"record Person(string First, string Last) -{ -} -record Student(string Id) -{ - void M($$Student p) - { - } -} -", MainDescription("record Student")); + """ + record Person(string First, string Last) + { + } + record Student(string Id) + { + void M($$Student p) + { + } + } + + """, MainDescription("record Student")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/44904")] public async Task QuickInfoRecord_BaseTypeList() { - await TestAsync(@" -record Person(string First, string Last); -record Student(int Id) : $$Person(null, null); -", MainDescription("Person.Person(string First, string Last)")); + await TestAsync(""" + + record Person(string First, string Last); + record Student(int Id) : $$Person(null, null); + + """, MainDescription("Person.Person(string First, string Last)")); } [Fact] public async Task QuickInfoClass_BaseTypeList() { - await TestAsync(@" -class Person(string First, string Last); -class Student(int Id) : $$Person(null, null); -", MainDescription("Person.Person(string First, string Last)")); + await TestAsync(""" + + class Person(string First, string Last); + class Student(int Id) : $$Person(null, null); + + """, MainDescription("Person.Person(string First, string Last)")); } [Fact] public async Task QuickInfo_BaseConstructorInitializer() { - await TestAsync(@" -public class Person { public Person(int id) { } } -public class Student : Person { public Student() : $$base(0) { } } -", MainDescription("Person.Person(int id)")); + await TestAsync(""" + + public class Person { public Person(int id) { } } + public class Student : Person { public Student() : $$base(0) { } } + + """, MainDescription("Person.Person(int id)")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/57031")] public async Task QuickInfo_DotInInvocation() { - await TestAsync(@" -public class C -{ - public void M(int a) { } - public void M(int a, params int[] b) { } -} + await TestAsync(""" -class Program -{ - static void Main() - { - var c = new C(); - c$$.M(1, 2); - } -}", + public class C + { + public void M(int a) { } + public void M(int a, params int[] b) { } + } + + class Program + { + static void Main() + { + var c = new C(); + c$$.M(1, 2); + } + } + """, MainDescription($"void C.M(int a, params int[] b) (+ 1 {FeaturesResources.overload})")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/57031")] public async Task QuickInfo_BeforeMemberNameInInvocation() { - await TestAsync(@" -public class C -{ - public void M(int a) { } - public void M(int a, params int[] b) { } -} + await TestAsync(""" -class Program -{ - static void Main() - { - var c = new C(); - c.$$M(1, 2); - } -}", + public class C + { + public void M(int a) { } + public void M(int a, params int[] b) { } + } + + class Program + { + static void Main() + { + var c = new C(); + c.$$M(1, 2); + } + } + """, MainDescription($"void C.M(int a, params int[] b) (+ 1 {FeaturesResources.overload})")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/57031")] public async Task QuickInfo_AfterMemberNameInInvocation() { - await TestAsync(@" -public class C -{ - public void M(int a) { } - public void M(int a, params int[] b) { } -} + await TestAsync(""" -class Program -{ - static void Main() - { - var c = new C(); - c.M$$(1, 2); - } -}", + public class C + { + public void M(int a) { } + public void M(int a, params int[] b) { } + } + + class Program + { + static void Main() + { + var c = new C(); + c.M$$(1, 2); + } + } + """, MainDescription($"void C.M(int a, params int[] b) (+ 1 {FeaturesResources.overload})")); } @@ -7749,12 +8874,14 @@ public async Task QuickInfoRecordClass() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"record class Person(string First, string Last) -{ - void M($$Person p) - { - } -}", MainDescription("record Person")); + """ + record class Person(string First, string Last) + { + void M($$Person p) + { + } + } + """, MainDescription("record Person")); } [Fact] @@ -7762,12 +8889,14 @@ public async Task QuickInfoRecordStruct() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"record struct Person(string First, string Last) -{ - void M($$Person p) - { - } -}", MainDescription("record struct Person")); + """ + record struct Person(string First, string Last) + { + void M($$Person p) + { + } + } + """, MainDescription("record struct Person")); } [Fact] @@ -7775,12 +8904,14 @@ public async Task QuickInfoReadOnlyRecordStruct() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"readonly record struct Person(string First, string Last) -{ - void M($$Person p) - { - } -}", MainDescription("readonly record struct Person")); + """ + readonly record struct Person(string First, string Last) + { + void M($$Person p) + { + } + } + """, MainDescription("readonly record struct Person")); } [Fact] @@ -7788,33 +8919,55 @@ public async Task QuickInfoRecordProperty() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"/// The person's first name. -record Person(string First, string Last) -{ - void M(Person p) - { - _ = p.$$First; - } -}", + """ + /// The person's first name. + record Person(string First, string Last) + { + void M(Person p) + { + _ = p.$$First; + } + } + """, MainDescription("string Person.First { get; init; }"), Documentation("The person's first name.")); } + [Fact] + public async Task QuickInfoFieldKeyword() + { + await TestWithOptionsAsync( + Options.Regular.WithLanguageVersion(LanguageVersion.Preview), + """ + class C + { + int Prop + { + get => $$field; + set => field = value; + } + } + """, +MainDescription("(field) int C.Prop.field")); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51615")] public async Task TestVarPatternOnVarKeyword() { await TestAsync( -@"class C -{ - string M() { } + """ + class C + { + string M() { } - void M2() - { - if (M() is va$$r x && x.Length > 0) - { - } - } -}", + void M2() + { + if (M() is va$$r x && x.Length > 0) + { + } + } + } + """, MainDescription("class System.String")); } @@ -7822,17 +8975,19 @@ void M2() public async Task TestVarPatternOnVariableItself() { await TestAsync( -@"class C -{ - string M() { } + """ + class C + { + string M() { } - void M2() - { - if (M() is var x$$ && x.Length > 0) - { - } - } -}", + void M2() + { + if (M() is var x$$ && x.Length > 0) + { + } + } + } + """, MainDescription($"({FeaturesResources.local_variable}) string? x")); } @@ -7840,15 +8995,17 @@ void M2() public async Task TestVarPatternOnVarKeyword_InListPattern() { await TestAsync( -@"class C -{ - void M(char[] array) - { - if (array is [ va$$r one ]) - { - } - } -}", + """ + class C + { + void M(char[] array) + { + if (array is [ va$$r one ]) + { + } + } + } + """, MainDescription("struct System.Char")); } @@ -7856,15 +9013,17 @@ void M(char[] array) public async Task TestVarPatternOnVariableItself_InListPattern() { await TestAsync( -@"class C -{ - void M(char[] array) - { - if (array is [ var o$$ne ]) - { - } - } -}", + """ + class C + { + void M(char[] array) + { + if (array is [ var o$$ne ]) + { + } + } + } + """, MainDescription($"({FeaturesResources.local_variable}) char one")); } @@ -7872,15 +9031,17 @@ void M(char[] array) public async Task TestVarPatternOnVarKeyword_InSlicePattern() { await TestAsync( -@"class C -{ - void M(char[] array) - { - if (array is [..va$$r one ]) - { - } - } -}" + TestSources.Index + TestSources.Range, + """ + class C + { + void M(char[] array) + { + if (array is [..va$$r one ]) + { + } + } + } + """ + TestSources.Index + TestSources.Range, MainDescription("char[]")); } @@ -7888,15 +9049,17 @@ void M(char[] array) public async Task TestVarPatternOnVariableItself_InSlicePattern() { await TestAsync( -@"class C -{ - void M(char[] array) - { - if (array is [ ..var o$$ne ]) - { - } - } -}" + TestSources.Index + TestSources.Range, + """ + class C + { + void M(char[] array) + { + if (array is [ ..var o$$ne ]) + { + } + } + } + """ + TestSources.Index + TestSources.Range, MainDescription($"({FeaturesResources.local_variable}) char[]? one")); } @@ -7904,73 +9067,87 @@ void M(char[] array) public async Task TestDocumentationCData() { var markup = -@"using I$$ = IGoo; -/// -/// summary for interface IGoo -/// y = null; -/// ]]> -/// -interface IGoo { }"; + """ + using I$$ = IGoo; + /// + /// summary for interface IGoo + /// y = null; + /// ]]> + /// + interface IGoo { } + """; await TestAsync(markup, MainDescription("interface IGoo"), - Documentation(@"summary for interface IGoo + Documentation(""" + summary for interface IGoo -List y = null;")); + List y = null; + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37503")] public async Task DoNotNormalizeWhitespaceForCode() { var markup = -@"using I$$ = IGoo; -/// -/// Normalize this, and Also this -/// -/// line 1 -/// line 2 -/// -/// -interface IGoo { }"; + """ + using I$$ = IGoo; + /// + /// Normalize this, and Also this + /// + /// line 1 + /// line 2 + /// + /// + interface IGoo { } + """; await TestAsync(markup, MainDescription("interface IGoo"), - Documentation(@"Normalize this, and Also this + Documentation(""" + Normalize this, and Also this -line 1 -line 2")); + line 1 + line 2 + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/57262")] public async Task DoNotNormalizeLeadingWhitespaceForCode() { var markup = - @"using I$$ = IGoo; -/// -/// Normalize this, and Also this -/// -/// line 1 -/// line 2 -/// -/// -interface IGoo { }"; + """ + using I$$ = IGoo; + /// + /// Normalize this, and Also this + /// + /// line 1 + /// line 2 + /// + /// + interface IGoo { } + """; await TestAsync(markup, MainDescription("interface IGoo"), - Documentation(@"Normalize this, and Also this + Documentation(""" + Normalize this, and Also this -line 1 - line 2")); + line 1 + line 2 + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/57262")] public async Task ParsesEmptySummary() { var markup = - @"using I$$ = IGoo; -/// -interface IGoo { }"; + """ + using I$$ = IGoo; + /// + interface IGoo { } + """; await TestAsync(markup, MainDescription("interface IGoo"), @@ -7980,18 +9157,20 @@ await TestAsync(markup, [Fact] public async Task TestStaticAbstract_ImplicitImplementation() { - var code = @" -interface I1 -{ - /// Summary text - static abstract void M1(); -} + var code = """ -class C1_1 : I1 -{ - public static void $$M1() { } -} -"; + interface I1 + { + /// Summary text + static abstract void M1(); + } + + class C1_1 : I1 + { + public static void $$M1() { } + } + + """; await TestAsync( code, @@ -8002,23 +9181,25 @@ await TestAsync( [Fact] public async Task TestStaticAbstract_ImplicitImplementation_FromReference() { - var code = @" -interface I1 -{ - /// Summary text - static abstract void M1(); -} + var code = """ -class C1_1 : I1 -{ - public static void M1() { } -} + interface I1 + { + /// Summary text + static abstract void M1(); + } -class R -{ - public static void M() { C1_1.$$M1(); } -} -"; + class C1_1 : I1 + { + public static void M1() { } + } + + class R + { + public static void M() { C1_1.$$M1(); } + } + + """; await TestAsync( code, @@ -8029,18 +9210,20 @@ await TestAsync( [Fact] public async Task TestStaticAbstract_FromTypeParameterReference() { - var code = @" -interface I1 -{ - /// Summary text - static abstract void M1(); -} + var code = """ -class R -{ - public static void M() where T : I1 { T.$$M1(); } -} -"; + interface I1 + { + /// Summary text + static abstract void M1(); + } + + class R + { + public static void M() where T : I1 { T.$$M1(); } + } + + """; await TestAsync( code, @@ -8051,19 +9234,21 @@ await TestAsync( [Fact] public async Task TestStaticAbstract_ExplicitInheritdoc_ImplicitImplementation() { - var code = @" -interface I1 -{ - /// Summary text - static abstract void M1(); -} + var code = """ -class C1_1 : I1 -{ - /// - public static void $$M1() { } -} -"; + interface I1 + { + /// Summary text + static abstract void M1(); + } + + class C1_1 : I1 + { + /// + public static void $$M1() { } + } + + """; await TestAsync( code, @@ -8074,18 +9259,20 @@ await TestAsync( [Fact] public async Task TestStaticAbstract_ExplicitImplementation() { - var code = @" -interface I1 -{ - /// Summary text - static abstract void M1(); -} + var code = """ -class C1_1 : I1 -{ - static void I1.$$M1() { } -} -"; + interface I1 + { + /// Summary text + static abstract void M1(); + } + + class C1_1 : I1 + { + static void I1.$$M1() { } + } + + """; await TestAsync( code, @@ -8096,19 +9283,21 @@ await TestAsync( [Fact] public async Task TestStaticAbstract_ExplicitInheritdoc_ExplicitImplementation() { - var code = @" -interface I1 -{ - /// Summary text - static abstract void M1(); -} + var code = """ + + interface I1 + { + /// Summary text + static abstract void M1(); + } -class C1_1 : I1 -{ - /// - static void I1.$$M1() { } -} -"; + class C1_1 : I1 + { + /// + static void I1.$$M1() { } + } + + """; await TestAsync( code, @@ -8121,10 +9310,12 @@ public async Task QuickInfoLambdaReturnType_01() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"class Program -{ - System.Delegate D = bo$$ol () => true; -}", + """ + class Program + { + System.Delegate D = bo$$ol () => true; + } + """, MainDescription("struct System.Boolean")); } @@ -8133,11 +9324,13 @@ public async Task QuickInfoLambdaReturnType_02() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"class A -{ - struct B { } - System.Delegate D = A.B$$ () => null; -}", + """ + class A + { + struct B { } + System.Delegate D = A.B$$ () => null; + } + """, MainDescription("struct A.B")); } @@ -8146,13 +9339,15 @@ public async Task QuickInfoLambdaReturnType_03() { await TestWithOptionsAsync( Options.Regular.WithLanguageVersion(LanguageVersion.CSharp9), -@"class A -{ -} -struct B -{ - System.Delegate D = A () => null; -}", + """ + class A + { + } + struct B + { + System.Delegate D = A () => null; + } + """, MainDescription("struct B")); } @@ -8160,30 +9355,36 @@ struct B public async Task TestNormalFuncSynthesizedLambdaType() { await TestAsync( -@"class C -{ - void M() - { - $$var v = (int i) => i.ToString(); - } -}", + """ + class C + { + void M() + { + $$var v = (int i) => i.ToString(); + } + } + """, MainDescription("delegate TResult System.Func(T arg)"), - TypeParameterMap($@" -T {FeaturesResources.is_} int -TResult {FeaturesResources.is_} string")); + TypeParameterMap($""" + + T {FeaturesResources.is_} int + TResult {FeaturesResources.is_} string + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58871")] public async Task TestInferredNonAnonymousDelegateType1() { await TestAsync( -@"class C -{ - void M() - { - $$var v = (int i) => i.ToString(); - } -}", + """ + class C + { + void M() + { + $$var v = (int i) => i.ToString(); + } + } + """, MainDescription("delegate TResult System.Func(T arg)"), AnonymousTypes("")); } @@ -8192,13 +9393,15 @@ void M() public async Task TestAnonymousSynthesizedLambdaType() { await TestAsync( -@"class C -{ - void M() - { - $$var v = (ref int i) => i.ToString(); - } -}", + """ + class C + { + void M() + { + $$var v = (ref int i) => i.ToString(); + } + } + """, MainDescription("delegate string (ref int arg)"), AnonymousTypes("")); } @@ -8207,120 +9410,140 @@ void M() public async Task TestAnonymousSynthesizedLambdaType2() { await TestAsync( -@"class C -{ - void M() - { - var $$v = (ref int i) => i.ToString(); - } -}", + """ + class C + { + void M() + { + var $$v = (ref int i) => i.ToString(); + } + } + """, MainDescription($"({FeaturesResources.local_variable}) 'a v"), AnonymousTypes( -$@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} delegate string (ref int arg)")); + $""" + + {FeaturesResources.Types_colon} + 'a {FeaturesResources.is_} delegate string (ref int arg) + """)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58871")] public async Task TestAnonymousSynthesizedLambdaType3() { await TestAsync( -@"class C -{ - void M() - { - var v = (ref int i) => i.ToString(); - $$Goo(v); - } + """ + class C + { + void M() + { + var v = (ref int i) => i.ToString(); + $$Goo(v); + } - T Goo(T t) => default; -}", + T Goo(T t) => default; + } + """, MainDescription("'a C.Goo<'a>('a t)"), AnonymousTypes( -$@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} delegate string (ref int arg)")); + $""" + + {FeaturesResources.Types_colon} + 'a {FeaturesResources.is_} delegate string (ref int arg) + """)); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] public async Task TestAnonymousSynthesizedLambdaType4() { await TestAsync( -@" -class C -{ - void M() - { - var lam = (int param = 42) => param + 1; - $$lam(); - } -} -", + """ + + class C + { + void M() + { + var lam = (int param = 42) => param + 1; + $$lam(); + } + } + + """, MainDescription($"({FeaturesResources.local_variable}) 'a lam"), AnonymousTypes( -$@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} delegate int (int arg = 42)")); + $""" + + {FeaturesResources.Types_colon} + 'a {FeaturesResources.is_} delegate int (int arg = 42) + """)); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] public async Task TestAnonymousSynthesizedLambdaType5() { await TestAsync( -@" -class C -{ - void M() - { - $$var lam = (int param = 42) => param; - } -} -", MainDescription("delegate int (int arg = 42)")); + """ + + class C + { + void M() + { + $$var lam = (int param = 42) => param; + } + } + + """, MainDescription("delegate int (int arg = 42)")); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] public async Task TestAnonymousSynthesizedLambdaType6() { await TestAsync( -@" -class C -{ - void M() - { - var lam = (i$$nt param = 42) => param; - } -} -", MainDescription("struct System.Int32")); + """ + + class C + { + void M() + { + var lam = (i$$nt param = 42) => param; + } + } + + """, MainDescription("struct System.Int32")); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] public async Task TestAnonymousSynthesizedLambdaType7() { await TestAsync( -@" -class C -{ - void M() - { - var lam = (int pa$$ram = 42) => param; - } -} -", MainDescription($"({FeaturesResources.parameter}) int param = 42")); + """ + + class C + { + void M() + { + var lam = (int pa$$ram = 42) => param; + } + } + + """, MainDescription($"({FeaturesResources.parameter}) int param = 42")); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] public async Task TestAnonymousSynthesizedLambdaType8() { await TestAsync( -@" -class C -{ - void M() - { - var lam = (int param = 4$$2) => param; - } -} -", MainDescription("struct System.Int32")); + """ + + class C + { + void M() + { + var lam = (int param = 4$$2) => param; + } + } + + """, MainDescription("struct System.Int32")); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] @@ -8393,11 +9616,13 @@ void M() public async Task TestSingleTupleType() { await TestInClassAsync( -@"void M((int x, string y) t) { } - void N() - { - $$M(default); - }", + """ + void M((int x, string y) t) { } + void N() + { + $$M(default); + } + """, MainDescription(@"void C.M((int x, string y) t)"), NoTypeParameterMap, AnonymousTypes(string.Empty)); @@ -8407,27 +9632,33 @@ void N() public async Task TestMultipleTupleTypesSameType() { await TestInClassAsync( -@"void M((int x, string y) s, (int x, string y) t) { } - void N() - { - $$M(default); - }", + """ + void M((int x, string y) s, (int x, string y) t) { } + void N() + { + $$M(default); + } + """, MainDescription(@"void C.M('a s, 'a t)"), NoTypeParameterMap, - AnonymousTypes($@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} (int x, string y)")); + AnonymousTypes($""" + + {FeaturesResources.Types_colon} + 'a {FeaturesResources.is_} (int x, string y) + """)); } [Fact] public async Task TestMultipleTupleTypesDifferentTypes1() { await TestInClassAsync( -@"void M((int x, string y) s, (int a, string b) u) { } - void N() - { - $$M(default); - }", + """ + void M((int x, string y) s, (int a, string b) u) { } + void N() + { + $$M(default); + } + """, MainDescription(@"void C.M((int x, string y) s, (int a, string b) u)"), NoTypeParameterMap); } @@ -8436,94 +9667,116 @@ void N() public async Task TestMultipleTupleTypesDifferentTypes2() { await TestInClassAsync( -@"void M((int x, string y) s, (int x, string y) t, (int a, string b) u, (int a, string b) v) { } - void N() - { - $$M(default); - }", + """ + void M((int x, string y) s, (int x, string y) t, (int a, string b) u, (int a, string b) v) { } + void N() + { + $$M(default); + } + """, MainDescription(@"void C.M('a s, 'a t, 'b u, 'b v)"), NoTypeParameterMap, - AnonymousTypes($@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} (int x, string y) - 'b {FeaturesResources.is_} (int a, string b)")); + AnonymousTypes($""" + + {FeaturesResources.Types_colon} + 'a {FeaturesResources.is_} (int x, string y) + 'b {FeaturesResources.is_} (int a, string b) + """)); } [Fact] public async Task TestMultipleTupleTypesDifferentTypes3() { await TestInClassAsync( -@"void M((int x, string y) s, (int x, string y) t, (int a, string b) u) { } - void N() - { - $$M(default); - }", + """ + void M((int x, string y) s, (int x, string y) t, (int a, string b) u) { } + void N() + { + $$M(default); + } + """, MainDescription(@"void C.M('a s, 'a t, 'b u)"), NoTypeParameterMap, - AnonymousTypes($@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} (int x, string y) - 'b {FeaturesResources.is_} (int a, string b)")); + AnonymousTypes($""" + + {FeaturesResources.Types_colon} + 'a {FeaturesResources.is_} (int x, string y) + 'b {FeaturesResources.is_} (int a, string b) + """)); } [Fact] public async Task TestMultipleTupleTypesInference() { await TestInClassAsync( -@"T M(T t) { } - void N() - { - (int a, string b) x = default; - $$M(x); - }", + """ + T M(T t) { } + void N() + { + (int a, string b) x = default; + $$M(x); + } + """, MainDescription(@"'a C.M<'a>('a t)"), NoTypeParameterMap, - AnonymousTypes($@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} (int a, string b)")); + AnonymousTypes($""" + + {FeaturesResources.Types_colon} + 'a {FeaturesResources.is_} (int a, string b) + """)); } [Fact] public async Task TestAnonymousTypeWithTupleTypesInference1() { await TestInClassAsync( -@"T M(T t) { } - void N() - { - var v = new { x = default((int a, string b)) }; - $$M(v); - }", + """ + T M(T t) { } + void N() + { + var v = new { x = default((int a, string b)) }; + $$M(v); + } + """, MainDescription(@"'a C.M<'a>('a t)"), NoTypeParameterMap, - AnonymousTypes($@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} new {{ (int a, string b) x }}")); + AnonymousTypes($$""" + + {{FeaturesResources.Types_colon}} + 'a {{FeaturesResources.is_}} new { (int a, string b) x } + """)); } [Fact] public async Task TestAnonymousTypeWithTupleTypesInference2() { await TestInClassAsync( -@"T M(T t) { } - void N() - { - var v = new { x = default((int a, string b)), y = default((int a, string b)) }; - $$M(v); - }", + """ + T M(T t) { } + void N() + { + var v = new { x = default((int a, string b)), y = default((int a, string b)) }; + $$M(v); + } + """, MainDescription(@"'a C.M<'a>('a t)"), NoTypeParameterMap, - AnonymousTypes($@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} new {{ 'b x, 'b y }} - 'b {FeaturesResources.is_} (int a, string b)")); + AnonymousTypes($$""" + + {{FeaturesResources.Types_colon}} + 'a {{FeaturesResources.is_}} new { 'b x, 'b y } + 'b {{FeaturesResources.is_}} (int a, string b) + """)); } [Fact] public async Task TestInRawStringInterpolation_SingleLine() { await TestInMethodAsync( -@"var x = 1; -var s = $""""""Hello world {$$x}""""""", + """" + var x = 1; + var s = $"""Hello world {$$x}""" + """", MainDescription($"({FeaturesResources.local_variable}) int x")); } @@ -8531,8 +9784,10 @@ await TestInMethodAsync( public async Task TestInRawStringInterpolation_SingleLine_MultiBrace() { await TestInMethodAsync( -@"var x = 1; -var s = ${|#0:|}$""""""Hello world {{$$x}}""""""", + """" + var x = 1; + var s = ${|#0:|}$"""Hello world {{$$x}}""" + """", MainDescription($"({FeaturesResources.local_variable}) int x")); } @@ -8540,18 +9795,24 @@ await TestInMethodAsync( public async Task TestInRawStringLiteral_SingleLine_Const() { await TestInClassAsync( -@"const string $$s = """"""Hello world""""""", - MainDescription(@$"({FeaturesResources.constant}) string C.s = """"""Hello world""""""")); + """" + const string $$s = """Hello world""" + """", + MainDescription($"""" + ({FeaturesResources.constant}) string C.s = """Hello world""" + """")); } [Fact] public async Task TestInRawStringInterpolation_MultiLine() { await TestInMethodAsync( -@"var x = 1; -var s = $"""""" -Hello world {$$x} -""""""", + """" + var x = 1; + var s = $""" + Hello world {$$x} + """ + """", MainDescription($"({FeaturesResources.local_variable}) int x")); } @@ -8559,10 +9820,12 @@ Hello world {$$x} public async Task TestInRawStringInterpolation_MultiLine_MultiBrace() { await TestInMethodAsync( -@"var x = 1; -var s = ${|#0:|}$"""""" -Hello world {{$$x}} -""""""", + """" + var x = 1; + var s = ${|#0:|}$""" + Hello world {{$$x}} + """ + """", MainDescription($"({FeaturesResources.local_variable}) int x")); } @@ -8570,23 +9833,29 @@ Hello world {{$$x}} public async Task TestInRawStringLiteral_MultiLine_Const() { await TestInClassAsync( -@"const string $$s = """""" - Hello world - """"""", - MainDescription(@$"({FeaturesResources.constant}) string C.s = """""" - Hello world - """"""")); + """" + const string $$s = """ + Hello world + """ + """", + MainDescription($"""" + ({FeaturesResources.constant}) string C.s = """ + Hello world + """ + """")); } [Fact] public async Task TestArgsInTopLevel() { var markup = -@" -forach (var arg in $$args) -{ -} -"; + """ + + forach (var arg in $$args) + { + } + + """; await TestWithOptionsAsync( Options.Regular, markup, @@ -8597,17 +9866,19 @@ await TestWithOptionsAsync( public async Task TestArgsInNormalProgram() { var markup = -@" -class Program -{ - static void Main(string[] args) - { - foreach (var arg in $$args) - { - } - } -} -"; + """ + + class Program + { + static void Main(string[] args) + { + foreach (var arg in $$args) + { + } + } + } + + """; await TestAsync(markup, MainDescription($"({FeaturesResources.parameter}) string[] args")); @@ -8616,13 +9887,15 @@ await TestAsync(markup, [Fact] public async Task TestParameterInMethodAttributeNameof() { - var source = @" -class Program -{ - [My(nameof($$s))] - void M(string s) { } -} -"; + var source = """ + + class Program + { + [My(nameof($$s))] + void M(string s) { } + } + + """; await TestWithOptionsAsync(Options.Regular.WithLanguageVersion(LanguageVersion.CSharp11), source, MainDescription($"({FeaturesResources.parameter}) string s")); } @@ -8630,12 +9903,14 @@ await TestWithOptionsAsync(Options.Regular.WithLanguageVersion(LanguageVersion.C [Fact] public async Task TestParameterInMethodParameterAttributeNameof() { - var source = @" -class Program -{ - void M([My(nameof($$s))] string s) { } -} -"; + var source = """ + + class Program + { + void M([My(nameof($$s))] string s) { } + } + + """; await TestWithOptionsAsync(Options.Regular.WithLanguageVersion(LanguageVersion.CSharp11), source, MainDescription($"({FeaturesResources.parameter}) string s")); } @@ -8643,16 +9918,18 @@ await TestWithOptionsAsync(Options.Regular.WithLanguageVersion(LanguageVersion.C [Fact] public async Task TestParameterInLocalFunctionAttributeNameof() { - var source = @" -class Program -{ - void M() - { - [My(nameof($$s))] - void local(string s) { } - } -} -"; + var source = """ + + class Program + { + void M() + { + [My(nameof($$s))] + void local(string s) { } + } + } + + """; await TestWithOptionsAsync(Options.Regular.WithLanguageVersion(LanguageVersion.CSharp11), source, MainDescription($"({FeaturesResources.parameter}) string s")); } @@ -8661,20 +9938,22 @@ await TestWithOptionsAsync(Options.Regular.WithLanguageVersion(LanguageVersion.C public async Task TestScopedParameter() { var source = -@"ref struct R { } -class Program -{ - static void F(R r1, scoped R r2, ref R r3, scoped ref R r4, in R r5, scoped in R r6, out R r7, scoped out R r8) - { - r7 = default; - r8 = default; - } - static void Main() - { - R r = default; - $$F(r, r, ref r, ref r, r, r, out r, out r); - } -}"; + """ + ref struct R { } + class Program + { + static void F(R r1, scoped R r2, ref R r3, scoped ref R r4, in R r5, scoped in R r6, out R r7, scoped out R r8) + { + r7 = default; + r8 = default; + } + static void Main() + { + R r = default; + $$F(r, r, ref r, ref r, r, r, out r, out r); + } + } + """; await TestAsync(source, MainDescription($"void Program.F(R r1, scoped R r2, ref R r3, scoped ref R r4, in R r5, scoped in R r6, out R r7, out R r8)")); } @@ -8683,15 +9962,17 @@ await TestAsync(source, public async Task TestScopedLocal() { var source = -@"class Program -{ - static void Main() - { - int i = 0; - scoped ref int r = ref i; - i = $$r; - } -}"; + """ + class Program + { + static void Main() + { + int i = 0; + scoped ref int r = ref i; + i = $$r; + } + } + """; await TestAsync(source, MainDescription($"({FeaturesResources.local_variable}) scoped ref int r")); } @@ -8743,7 +10024,7 @@ await TestAsync(source, public async Task TestUsingAliasToType1() { var source = -@"using X = $$int;"; + @"using X = $$int;"; await TestAsync(source, MainDescription($"struct System.Int32")); } @@ -8752,7 +10033,7 @@ await TestAsync(source, public async Task TestUsingAliasToType1_A() { var source = -@"using $$X = int;"; + @"using $$X = int;"; await TestAsync(source, MainDescription($"struct System.Int32")); } @@ -8761,7 +10042,7 @@ await TestAsync(source, public async Task TestUsingAliasToType2() { var source = -@"using X = ($$int a, int b);"; + @"using X = ($$int a, int b);"; await TestAsync(source, MainDescription($"struct System.Int32")); } @@ -8770,7 +10051,7 @@ await TestAsync(source, public async Task TestUsingAliasToType2_A() { var source = -@"using $$X = (int a, int b);"; + @"using $$X = (int a, int b);"; await TestAsync(source, MainDescription($"(int a, int b)")); } @@ -8779,7 +10060,7 @@ await TestAsync(source, public async Task TestUsingAliasToType3() { var source = -@"using X = $$(int a, int b);"; + @"using X = $$(int a, int b);"; await TestAsync(source); } @@ -8787,7 +10068,7 @@ public async Task TestUsingAliasToType3() public async Task TestUsingAliasToType4() { var source = -@"using unsafe X = $$delegate*;"; + @"using unsafe X = $$delegate*;"; await TestAsync(source); } @@ -8795,7 +10076,7 @@ public async Task TestUsingAliasToType4() public async Task TestUsingAliasToType4_A() { var source = -@"using unsafe $$X = delegate*;"; + @"using unsafe $$X = delegate*;"; await TestAsync(source, MainDescription($"delegate*")); } @@ -8804,7 +10085,7 @@ await TestAsync(source, public async Task TestUsingAliasToType5() { var source = -@"using unsafe X = $$int*;"; + @"using unsafe X = $$int*;"; await TestAsync(source, MainDescription($"struct System.Int32")); } @@ -8813,7 +10094,7 @@ await TestAsync(source, public async Task TestUsingAliasToType5_A() { var source = -@"using unsafe $$X = int*;"; + @"using unsafe $$X = int*;"; await TestAsync(source, MainDescription($"int*")); } @@ -8822,7 +10103,7 @@ await TestAsync(source, public async Task TestCollectionExpression_Start() { var source = -"int[] x = $$[1, 2]"; + "int[] x = $$[1, 2]"; await TestAsync(source, MainDescription($"int[]")); } @@ -8831,7 +10112,7 @@ await TestAsync(source, public async Task TestCollectionExpression_Middle() { var source = -"int[] x = [1 $$, 2]"; + "int[] x = [1 $$, 2]"; await TestAsync(source); } @@ -8839,7 +10120,7 @@ public async Task TestCollectionExpression_Middle() public async Task TestCollectionExpression_End() { var source = -"int[] x = [1, 2]$$"; + "int[] x = [1, 2]$$"; await TestAsync(source, MainDescription($"int[]")); } @@ -8848,7 +10129,7 @@ await TestAsync(source, public async Task TestCollectionExpression_Start_Typeless() { var source = -"var x = $$[1, 2]"; + "var x = $$[1, 2]"; await TestAsync(source); } @@ -8867,9 +10148,11 @@ await VerifyWithMscorlib45Async(markup, new[] { MainDescription(description), AnonymousTypes( -$@" -{FeaturesResources.Types_colon} - 'a {FeaturesResources.is_} new {{ string @string }}") + $$""" + + {{FeaturesResources.Types_colon}} + 'a {{FeaturesResources.is_}} new { string @string } + """) }); } diff --git a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs index 20c4c56035d2f..c91609f2d7a65 100644 --- a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Xml.Linq; using Microsoft.CodeAnalysis.Editor.CSharp.RawStringLiteral; @@ -30,7 +31,7 @@ public RawStringLiteralTestState(XElement workspaceElement) Single(c => c is RawStringLiteralCommandHandler); } - public static RawStringLiteralTestState CreateTestState(string markup, bool withSpansOnly = false) + public static RawStringLiteralTestState CreateTestState([StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, bool withSpansOnly = false) => new(GetWorkspaceXml(markup, withSpansOnly)); public static XElement GetWorkspaceXml(string markup, bool withSpansOnly) @@ -45,7 +46,7 @@ public static XElement GetWorkspaceXml(string markup, bool withSpansOnly) """); } - internal void AssertCodeIs(string expectedCode, bool withSpansOnly = false) + internal void AssertCodeIs([StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string expectedCode, bool withSpansOnly = false) { if (withSpansOnly) expectedCode = expectedCode.Replace("$", "\uD7FF"); @@ -105,7 +106,7 @@ public void TestReturnInSixQuotes() public void TestReturnInSixQuotesWithSemicolonAfter() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = """"""$$"""""";"); + """"var v = """$$""";""""); testState.SendReturn(handled: true); testState.AssertCodeIs( @@ -188,7 +189,7 @@ public void TestReturnInSixQuotesAsArgument1() public void TestReturnInSixQuotesAsArgument2() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = WriteLine(""""""$$"""""")"); + """"var v = WriteLine("""$$""")""""); testState.SendReturn(handled: true); testState.AssertCodeIs( @@ -203,7 +204,7 @@ public void TestReturnInSixQuotesAsArgument2() public void TestReturnInSixQuotesAsArgument3() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = WriteLine(""""""$$"""""");"); + """"var v = WriteLine("""$$""");""""); testState.SendReturn(handled: true); testState.AssertCodeIs( @@ -275,7 +276,7 @@ public void TestReturnInSixQuotesAsArgument6() public void TestReturnInSixQuotesWithSemicolonAfter_Interpolated() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = $""""""$$"""""";"); + """"var v = $"""$$""";""""); testState.SendReturn(handled: true); testState.AssertCodeIs( @@ -305,7 +306,7 @@ public void TestReturnInSixQuotesNotAtMiddle_Interpolated() public void TestReturnEndOfFile() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = """"""$$"); + """"var v = """$$""""); testState.SendReturn(handled: false); } @@ -314,7 +315,207 @@ public void TestReturnEndOfFile() public void TestReturnInEmptyFile() { using var testState = RawStringLiteralTestState.CreateTestState( -@"$$"); + "$$"); + + testState.SendReturn(handled: false); + } + + [WpfFact] + public void TestReturnAfterThreeQuotesFollowingText() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = """$$following text"""; + """"); + + testState.SendReturn(handled: true); + testState.AssertCodeIs( + """" + var v = """ + $$following text + """; + """"); + } + + [WpfFact] + public void TestReturnAfterThreeQuotesFollowingText_Interpolated() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = $"""$$following text {0}"""; + """"); + + testState.SendReturn(handled: true); + testState.AssertCodeIs( + """" + var v = $""" + $$following text {0} + """; + """"); + } + + [WpfFact] + public void TestReturnAfterTextInRawStringFollowingText() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = """before text$$following text"""; + """"); + + testState.SendReturn(handled: true); + testState.AssertCodeIs( + """" + var v = """ + before text + $$following text + """; + """"); + } + + [WpfFact] + public void TestReturnAfterTextInRawStringFollowingText_Interpolated() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = $"""before text$$following text {0}"""; + """"); + + testState.SendReturn(handled: true); + testState.AssertCodeIs( + """" + var v = $""" + before text + $$following text {0} + """; + """"); + } + + [WpfFact] + public void TestReturnOnInterpolationOpenBraceInRawString() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = $"""before text$${0} following text"""; + """"); + + testState.SendReturn(handled: true); + testState.AssertCodeIs( + """" + var v = $""" + before text + $${0} following text + """; + """"); + } + + [WpfFact] + public void TestReturnAfterInterpolationOpenBraceInRawString() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = $"""before text{0}$$following text"""; + """"); + + testState.SendReturn(handled: true); + testState.AssertCodeIs( + """" + var v = $""" + before text{0} + $$following text + """; + """"); + } + + [WpfFact] + public void TestReturnInsideInterpolationInRawString1() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = $"""before text{$$0} following text"""; + """"); + + testState.SendReturn(handled: false); + } + + [WpfFact] + public void TestReturnInsideInterpolationInRawString2() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = $"""before text{0$$} following text"""; + """"); + + testState.SendReturn(handled: false); + } + + [WpfFact] + public void TestReturnWithinOpenBracesInterpolationInRawString() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = $$$"""before text{[||]{{0}}} following text"""; + """"); + + testState.SendReturn(handled: false); + } + + [WpfFact] + public void TestReturnWithinCloseBracesInterpolationInRawString() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = $$$"""before text{{{0}}[||]} following text"""; + """"); + + testState.SendReturn(handled: false); + } + + [WpfFact] + public void TestReturnBeforeEndQuotesInRawString() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = """before text$$"""; + """"); + + testState.SendReturn(handled: true); + testState.AssertCodeIs( + """" + var v = """ + before text + $$ + """; + """"); + } + + [WpfFact] + public void TestReturnWithinEndQuotesInRawString() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = """before text""$$"; + """"); + + testState.SendReturn(handled: false); + } + + [WpfFact] + public void TestReturnAfterStartQuotesInMultilineRawString() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = """$$ + """; + """"); + + testState.SendReturn(handled: false); + } + + [WpfFact] + public void TestReturnBeforeEndQuotesInMultilineRawString() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = """ + $$"""; + """"); + + testState.SendReturn(handled: false); + } + + [WpfFact] + public void TestReturnWithinEndQuotesInMultilineRawString() + { + using var testState = RawStringLiteralTestState.CreateTestState("""" + var v = """ + ""$$"; + """"); testState.SendReturn(handled: false); } @@ -327,7 +528,7 @@ public void TestReturnInEmptyFile() public void TestGenerateAtEndOfFile() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = """"$$"); + """var v = ""$$"""); testState.SendTypeChar('"'); testState.AssertCodeIs( @@ -340,18 +541,18 @@ public void TestGenerateAtEndOfFile() public void TestGenerateWithSemicolonAfter() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = """"$$;"); + """var v = ""$$;"""); testState.SendTypeChar('"'); testState.AssertCodeIs( -@"var v = """"""$$"""""";"); + """"var v = """$$""";""""); } [WpfFact] public void TestGenerateWithInterpolatedString() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = $""""$$"); + """var v = $""$$"""); testState.SendTypeChar('"'); testState.AssertCodeIs( @@ -364,70 +565,70 @@ public void TestGenerateWithInterpolatedString() public void TestGenerateWithInterpolatedString_TwoDollarSigns() { using var testState = RawStringLiteralTestState.CreateTestState( -"""var v = $$""[||]""", withSpansOnly: true); + """var v = $$""[||]""", withSpansOnly: true); testState.SendTypeChar('"'); testState.AssertCodeIs( -"""" -var v = $$"""[||]""" -"""", withSpansOnly: true); + """" + var v = $$"""[||]""" + """", withSpansOnly: true); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/66538")] public void TestGenerateWithInterpolatedString_TwoDollarSigns_FourthDoubleQuote() { using var testState = RawStringLiteralTestState.CreateTestState( -""""var v = $$"""[||]""";"""", withSpansOnly: true); + """"var v = $$"""[||]""";"""", withSpansOnly: true); testState.SendTypeChar('"'); testState.AssertCodeIs( -"""""var v = $$""""[||]"""";""""", withSpansOnly: true); + """""var v = $$""""[||]"""";""""", withSpansOnly: true); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/66538")] public void TestGenerateWithInterpolatedString_ThreeDollarSigns() { using var testState = RawStringLiteralTestState.CreateTestState( -"""var v = $$$""[||]""", withSpansOnly: true); + """var v = $$$""[||]""", withSpansOnly: true); testState.SendTypeChar('"'); testState.AssertCodeIs( -"""" -var v = $$$"""[||]""" -"""", withSpansOnly: true); + """" + var v = $$$"""[||]""" + """", withSpansOnly: true); } [WpfFact] public void TestNoGenerateWithVerbatimString() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = @""""$$"); + """var v = @""$$"""); testState.SendTypeChar('"'); testState.AssertCodeIs( -@"var v = @""""""$$"); + """"var v = @"""$$""""); } [WpfFact] public void TestNoGenerateWithVerbatimInterpolatedString1() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = @$""""$$"); + """var v = @$""$$"""); testState.SendTypeChar('"'); testState.AssertCodeIs( -@"var v = @$""""""$$"); + """"var v = @$"""$$""""); } [WpfFact] public void TestNoGenerateWithVerbatimInterpolatedString2() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = $@""""$$"); + """var v = $@""$$"""); testState.SendTypeChar('"'); testState.AssertCodeIs( -@"var v = $@""""""$$"); + """"var v = $@"""$$""""); } #endregion @@ -514,7 +715,7 @@ public void TestDoNotGrowEmptyInsideSixQuotesWhenNotInMiddle1() #region grow delimiters [WpfFact] - public void TestGrowDelimetersWhenEndExists_SingleLine() + public void TestGrowDelimitersWhenEndExists_SingleLine() { using var testState = RawStringLiteralTestState.CreateTestState( """" @@ -529,7 +730,7 @@ public void TestGrowDelimetersWhenEndExists_SingleLine() } [WpfFact] - public void TestGrowDelimetersWhenEndExists_MultiLine() + public void TestGrowDelimitersWhenEndExists_MultiLine() { using var testState = RawStringLiteralTestState.CreateTestState( """" @@ -548,8 +749,10 @@ public void TestGrowDelimetersWhenEndExists_MultiLine() } [WpfFact] - public void TestGrowDelimetersWhenEndExists_Interpolated() + public void TestGrowDelimitersWhenEndExists_Interpolated() { + // Delimiter is right + // Delimiter is not. using var testState = RawStringLiteralTestState.CreateTestState( """" var v = $"""$$ @@ -567,18 +770,18 @@ public void TestGrowDelimetersWhenEndExists_Interpolated() } [WpfFact] - public void TestDoNotGrowDelimetersWhenEndNotThere() + public void TestDoNotGrowDelimitersWhenEndNotThere() { using var testState = RawStringLiteralTestState.CreateTestState( -@"var v = """"""$$"); + """"var v = """$$""""); testState.SendTypeChar('"'); testState.AssertCodeIs( -@"var v = """"""""$$"); + """""var v = """"$$"""""); } [WpfFact] - public void TestDoNotGrowDelimetersWhenEndTooShort() + public void TestDoNotGrowDelimitersWhenEndTooShort() { using var testState = RawStringLiteralTestState.CreateTestState( """" @@ -601,8 +804,7 @@ public void TestDoNotGrowDelimetersWhenEndTooShort() [WpfFact] public void TestTypeQuoteEmptyFile() { - using var testState = RawStringLiteralTestState.CreateTestState( -@"$$"); + using var testState = RawStringLiteralTestState.CreateTestState("$$"); testState.SendTypeChar('"'); testState.AssertCodeIs( diff --git a/src/EditorFeatures/CSharpTest/Structure/RegionDirectiveStructureTests.cs b/src/EditorFeatures/CSharpTest/Structure/RegionDirectiveStructureTests.cs index 1453fbfc75817..80d95025a9fa7 100644 --- a/src/EditorFeatures/CSharpTest/Structure/RegionDirectiveStructureTests.cs +++ b/src/EditorFeatures/CSharpTest/Structure/RegionDirectiveStructureTests.cs @@ -115,4 +115,18 @@ static void Main(string[] args) await VerifyBlockSpansAsync(code, Region("span", "Region", autoCollapse: false, isDefaultCollapsed: true)); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75583")] + public async Task Trailing() + { + var code = """ + {|span:#region R$$1 + /* comment */ #endregion + /* comment */ #region R2 + #endregion|} + """; + + await VerifyBlockSpansAsync(code, + Region("span", "R1", autoCollapse: false, isDefaultCollapsed: true)); + } } diff --git a/src/EditorFeatures/CSharpTest/Structure/StringLiteralExpressionStructureTests.cs b/src/EditorFeatures/CSharpTest/Structure/StringLiteralExpressionStructureTests.cs index d2583d0062f88..51e532c65c114 100644 --- a/src/EditorFeatures/CSharpTest/Structure/StringLiteralExpressionStructureTests.cs +++ b/src/EditorFeatures/CSharpTest/Structure/StringLiteralExpressionStructureTests.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Structure; [Trait(Traits.Feature, Traits.Features.Outlining)] -public class StringLiteralExpressionStructureTests : AbstractCSharpSyntaxNodeStructureTests +public sealed class StringLiteralExpressionStructureTests : AbstractCSharpSyntaxNodeStructureTests { internal override AbstractSyntaxStructureProvider CreateProvider() => new StringLiteralExpressionStructureProvider(); diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs index 91222cfcb6840..549acf728a56a 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs @@ -156,6 +156,10 @@ private void OnTextViewClosed(object sender, EventArgs e) public async Task GetSuggestedActionCategoriesAsync( ISuggestedActionCategorySet requestedActionCategories, SnapshotSpan range, CancellationToken cancellationToken) { + // Make sure we're explicitly on the background, to do as much as possible in a non-blocking fashion. + await TaskScheduler.Default; + cancellationToken.ThrowIfCancellationRequested(); + // This function gets called immediately after operations like scrolling. We want to wait just a small // amount to ensure that we don't immediately start consuming CPU/memory which then impedes the very // action the user is trying to perform. To accomplish this, we wait 100ms. That's longer than normal @@ -177,12 +181,6 @@ private void OnTextViewClosed(object sender, EventArgs e) if (workspace == null) return null; - // never show light bulb if solution is not fully loaded yet - if (!await workspace.Services.GetRequiredService().IsFullyLoadedAsync(cancellationToken).ConfigureAwait(false)) - return null; - - cancellationToken.ThrowIfCancellationRequested(); - using var asyncToken = state.Target.Owner.OperationListener.BeginAsyncOperation(nameof(GetSuggestedActionCategoriesAsync)); var document = range.Snapshot.GetOpenTextDocumentInCurrentContextWithChanges(); if (document == null) diff --git a/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs b/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs index 138d4e1317667..26e47c2026d5c 100644 --- a/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs +++ b/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs @@ -89,10 +89,18 @@ public IEnumerable> GetTags(NormalizedSnapshotSpanC return []; } - private static IReadOnlyList> GetIntersectingTags(NormalizedSnapshotSpanCollection spans, TagSpanIntervalTree cachedTags) - => SegmentedListPool>.ComputeList( - static (args, tags) => args.cachedTags.AddIntersectingTagSpans(args.spans, tags), - (cachedTags, spans)); + private static IEnumerable> GetIntersectingTags(NormalizedSnapshotSpanCollection spans, TagSpanIntervalTree cachedTags) + { + using var pooledObject = SegmentedListPool.GetPooledList>(out var list); + + cachedTags.AddIntersectingTagSpans(spans, list); + + // Use yield return mechanism to allow the segmented list to get returned back to the + // pool after usage. This does cause an allocation for the yield state machinery, but + // that is better than not freeing a potentially large segmented list back to the pool. + foreach (var item in list) + yield return item; + } IEnumerable> IAccurateTagger.GetAllTags(NormalizedSnapshotSpanCollection spans, CancellationToken cancellationToken) => GetAllTags(spans, cancellationToken); diff --git a/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs b/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs index 36f255d8fd3fc..5c3157ba29539 100644 --- a/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs +++ b/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs @@ -48,7 +48,7 @@ protected override ITaggerEventSource CreateEventSource(ITextView textView, ITex TaggerEventSources.OnTextChanged(subjectBuffer)); } - protected override void AddSpansToTag(ITextView? textView, ITextBuffer subjectBuffer, ref TemporaryArray result) + protected override bool TryAddSpansToTag(ITextView? textView, ITextBuffer subjectBuffer, ref TemporaryArray result) { this.ThreadingContext.ThrowIfNotOnUIThread(); Contract.ThrowIfNull(textView); @@ -56,6 +56,10 @@ protected override void AddSpansToTag(ITextView? textView, ITextBuffer subjectBu // We only care about the cases where we have caret. if (textView.GetCaretPoint(subjectBuffer) is { } caret) result.Add(new SnapshotSpan(caret, 0)); + + // Note: we report 'true' unconditionally here. This is because we want to ensure that we still compute 'no tags' + // in the case that we don't have a caret point, as opposed to bailing out and keeping the current set of tags. + return true; } protected override async Task ProduceTagsAsync(TaggerContext context, DocumentSnapshotSpan spanToTag, int? caretPosition, CancellationToken cancellationToken) diff --git a/src/EditorFeatures/Core/DocumentationComments/AbstractDocumentationCommentCommandHandler.cs b/src/EditorFeatures/Core/DocumentationComments/AbstractDocumentationCommentCommandHandler.cs index f3084ed46cdb9..04db5a505e483 100644 --- a/src/EditorFeatures/Core/DocumentationComments/AbstractDocumentationCommentCommandHandler.cs +++ b/src/EditorFeatures/Core/DocumentationComments/AbstractDocumentationCommentCommandHandler.cs @@ -57,14 +57,14 @@ private char TriggerCharacter public string DisplayName => EditorFeaturesResources.Documentation_Comment; - private static DocumentationCommentSnippet? InsertOnCharacterTyped(IDocumentationCommentSnippetService service, SyntaxTree syntaxTree, SourceText text, int position, DocumentationCommentOptions options, CancellationToken cancellationToken) - => service.GetDocumentationCommentSnippetOnCharacterTyped(syntaxTree, text, position, options, cancellationToken); + private static DocumentationCommentSnippet? InsertOnCharacterTyped(IDocumentationCommentSnippetService service, ParsedDocument document, int position, DocumentationCommentOptions options, CancellationToken cancellationToken) + => service.GetDocumentationCommentSnippetOnCharacterTyped(document, position, options, cancellationToken); - private static DocumentationCommentSnippet? InsertOnEnterTyped(IDocumentationCommentSnippetService service, SyntaxTree syntaxTree, SourceText text, int position, DocumentationCommentOptions options, CancellationToken cancellationToken) - => service.GetDocumentationCommentSnippetOnEnterTyped(syntaxTree, text, position, options, cancellationToken); + private static DocumentationCommentSnippet? InsertOnEnterTyped(IDocumentationCommentSnippetService service, ParsedDocument document, int position, DocumentationCommentOptions options, CancellationToken cancellationToken) + => service.GetDocumentationCommentSnippetOnEnterTyped(document, position, options, cancellationToken); - private static DocumentationCommentSnippet? InsertOnCommandInvoke(IDocumentationCommentSnippetService service, SyntaxTree syntaxTree, SourceText text, int position, DocumentationCommentOptions options, CancellationToken cancellationToken) - => service.GetDocumentationCommentSnippetOnCommandInvoke(syntaxTree, text, position, options, cancellationToken); + private static DocumentationCommentSnippet? InsertOnCommandInvoke(IDocumentationCommentSnippetService service, ParsedDocument document, int position, DocumentationCommentOptions options, CancellationToken cancellationToken) + => service.GetDocumentationCommentSnippetOnCommandInvoke(document, position, options, cancellationToken); private static void ApplySnippet(DocumentationCommentSnippet snippet, ITextBuffer subjectBuffer, ITextView textView) { @@ -76,7 +76,7 @@ private static void ApplySnippet(DocumentationCommentSnippet snippet, ITextBuffe private bool CompleteComment( ITextBuffer subjectBuffer, ITextView textView, - Func getSnippetAction, + Func getSnippetAction, CancellationToken cancellationToken) { var caretPosition = textView.GetCaretPoint(subjectBuffer) ?? -1; @@ -100,7 +100,7 @@ private bool CompleteComment( var returnValue = false; foreach (var snapshot in snapshots) { - var snippet = getSnippetAction(service, parsedDocument.SyntaxTree, parsedDocument.Text, snapshot.Span.Start, options, cancellationToken); + var snippet = getSnippetAction(service, parsedDocument, snapshot.Span.Start, options, cancellationToken); if (snippet != null) { ApplySnippet(snippet, subjectBuffer, textView); @@ -209,9 +209,8 @@ public CommandState GetCommandState(InsertCommentCommandArgs args) var isValidTargetMember = false; _uiThreadOperationExecutor.Execute("IntelliSense", defaultDescription: "", allowCancellation: true, showProgress: false, action: c => { - var syntaxTree = document.GetRequiredSyntaxTreeSynchronously(c.UserCancellationToken); - var text = syntaxTree.GetText(c.UserCancellationToken); - isValidTargetMember = service.IsValidTargetMember(syntaxTree, text, caretPosition, c.UserCancellationToken); + var parsedDocument = ParsedDocument.CreateSynchronously(document, c.UserCancellationToken); + isValidTargetMember = service.IsValidTargetMember(parsedDocument, caretPosition, c.UserCancellationToken); }); return isValidTargetMember diff --git a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs index 93a157f97d471..c4d3dd98990c0 100644 --- a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs +++ b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs @@ -6,6 +6,7 @@ using System.Collections.Immutable; using System.Composition; using System.Diagnostics; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.BrokeredServices; @@ -13,6 +14,8 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Debugger.Contracts.HotReload; using Roslyn.Utilities; @@ -21,6 +24,7 @@ namespace Microsoft.CodeAnalysis.EditAndContinue; [Shared] [Export(typeof(IManagedHotReloadLanguageService))] +[Export(typeof(IManagedHotReloadLanguageService2))] [Export(typeof(IEditAndContinueSolutionProvider))] [Export(typeof(EditAndContinueLanguageService))] [ExportMetadata("UIContext", EditAndContinueUIContext.EncCapableProjectExistsInWorkspaceUIContextString)] @@ -33,7 +37,7 @@ internal sealed class EditAndContinueLanguageService( Lazy debuggerService, PdbMatchingSourceTextProvider sourceTextProvider, IDiagnosticsRefresher diagnosticRefresher, - IAsynchronousOperationListenerProvider listenerProvider) : IManagedHotReloadLanguageService, IEditAndContinueSolutionProvider + IAsynchronousOperationListenerProvider listenerProvider) : IManagedHotReloadLanguageService2, IEditAndContinueSolutionProvider { private sealed class NoSessionException : InvalidOperationException { @@ -242,6 +246,64 @@ public async ValueTask DiscardUpdatesAsync(CancellationToken cancellationToken) } } + public async ValueTask UpdateBaselinesAsync(ImmutableArray projectPaths, CancellationToken cancellationToken) + { + if (_disabled) + { + return; + } + + var currentDesignTimeSolution = GetCurrentDesignTimeSolution(); + var currentCompileTimeSolution = GetCurrentCompileTimeSolution(currentDesignTimeSolution); + + try + { + SolutionCommitted?.Invoke(currentDesignTimeSolution); + } + catch (Exception e) when (FatalError.ReportAndCatch(e)) + { + } + + _committedDesignTimeSolution = currentDesignTimeSolution; + var projectIds = await GetProjectIdsAsync(projectPaths, currentCompileTimeSolution, cancellationToken).ConfigureAwait(false); + + try + { + await GetDebuggingSession().UpdateBaselinesAsync(currentCompileTimeSolution, projectIds, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) + { + } + + foreach (var projectId in projectIds) + { + workspaceProvider.Value.Workspace.EnqueueUpdateSourceGeneratorVersion(projectId, forceRegeneration: false); + } + } + + private async ValueTask> GetProjectIdsAsync(ImmutableArray projectPaths, Solution solution, CancellationToken cancellationToken) + { + using var _ = ArrayBuilder.GetInstance(out var projectIds); + foreach (var path in projectPaths) + { + var projectId = solution.Projects.FirstOrDefault(project => project.FilePath == path)?.Id; + if (projectId != null) + { + projectIds.Add(projectId); + } + else + { + await _logger.LogAsync(new HotReloadLogMessage( + HotReloadVerbosity.Diagnostic, + $"Project with path '{path}' not found in the current solution.", + errorLevel: HotReloadDiagnosticErrorLevel.Warning), + cancellationToken).ConfigureAwait(false); + } + } + + return projectIds.ToImmutable(); + } + public async ValueTask EndSessionAsync(CancellationToken cancellationToken) { sessionState.IsSessionActive = false; @@ -309,7 +371,11 @@ public async ValueTask HasChangesAsync(string? sourceFilePath, Cancellatio } } - public async ValueTask GetUpdatesAsync(CancellationToken cancellationToken) + [Obsolete] + public ValueTask GetUpdatesAsync(CancellationToken cancellationToken) + => GetUpdatesAsync(runningProjects: [], cancellationToken); + + public async ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken) { if (_disabled) { @@ -319,7 +385,12 @@ public async ValueTask GetUpdatesAsync(CancellationToke var designTimeSolution = GetCurrentDesignTimeSolution(); var solution = GetCurrentCompileTimeSolution(designTimeSolution); var activeStatementSpanProvider = GetActiveStatementSpanProvider(solution); - var result = await GetDebuggingSession().EmitSolutionUpdateAsync(solution, activeStatementSpanProvider, cancellationToken).ConfigureAwait(false); + + using var _ = PooledHashSet.GetInstance(out var runningProjectPaths); + runningProjectPaths.AddAll(runningProjects); + + var runningProjectIds = solution.Projects.Where(p => p.FilePath != null && runningProjectPaths.Contains(p.FilePath)).Select(static p => p.Id).ToImmutableHashSet(); + var result = await GetDebuggingSession().EmitSolutionUpdateAsync(solution, runningProjectIds, activeStatementSpanProvider, cancellationToken).ConfigureAwait(false); // Only store the solution if we have any changes to apply, otherwise CommitUpdatesAsync/DiscardUpdatesAsync won't be called. if (result.ModuleUpdates.Status == ModuleUpdateStatus.Ready) @@ -329,6 +400,13 @@ public async ValueTask GetUpdatesAsync(CancellationToke UpdateApplyChangesDiagnostics(result.Diagnostics); - return new ManagedHotReloadUpdates(result.ModuleUpdates.Updates.FromContract(), result.GetAllDiagnostics().FromContract()); + return new ManagedHotReloadUpdates( + result.ModuleUpdates.Updates.FromContract(), + result.GetAllDiagnostics().FromContract(), + GetProjectPaths(result.ProjectsToRebuild), + GetProjectPaths(result.ProjectsToRestart)); + + ImmutableArray GetProjectPaths(ImmutableArray ids) + => ids.SelectAsArray(static (id, solution) => solution.GetRequiredProject(id).FilePath!, solution); } } diff --git a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs index f4071f8137054..be9aadc407671 100644 --- a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs +++ b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.Debugger.Contracts.HotReload; @@ -13,7 +15,7 @@ namespace Microsoft.CodeAnalysis.EditAndContinue; /// TODO (https://github.com/dotnet/roslyn/issues/72713): /// Once debugger is updated to use the brokered service, this class should be removed and should be exported directly. /// -internal sealed partial class ManagedEditAndContinueLanguageServiceBridge(EditAndContinueLanguageService service) : IManagedHotReloadLanguageService +internal sealed partial class ManagedEditAndContinueLanguageServiceBridge(EditAndContinueLanguageService service) : IManagedHotReloadLanguageService2 { public ValueTask StartSessionAsync(CancellationToken cancellationToken) => service.StartSessionAsync(cancellationToken); @@ -30,12 +32,19 @@ public ValueTask ExitBreakStateAsync(CancellationToken cancellationToken) public ValueTask OnCapabilitiesChangedAsync(CancellationToken cancellationToken) => service.OnCapabilitiesChangedAsync(cancellationToken); - public async ValueTask GetUpdatesAsync(CancellationToken cancellationToken) - => (await service.GetUpdatesAsync(cancellationToken).ConfigureAwait(false)); + [Obsolete] + public ValueTask GetUpdatesAsync(CancellationToken cancellationToken) + => service.GetUpdatesAsync(cancellationToken); + + public ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken) + => service.GetUpdatesAsync(runningProjects, cancellationToken); public ValueTask CommitUpdatesAsync(CancellationToken cancellationToken) => service.CommitUpdatesAsync(cancellationToken); + public ValueTask UpdateBaselinesAsync(ImmutableArray projectPaths, CancellationToken cancellationToken) + => service.UpdateBaselinesAsync(projectPaths, cancellationToken); + public ValueTask DiscardUpdatesAsync(CancellationToken cancellationToken) => service.DiscardUpdatesAsync(cancellationToken); diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptRequestExecutionQueueProvider.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptRequestExecutionQueueProvider.cs index e2097d53c6631..d4c90787529e1 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptRequestExecutionQueueProvider.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptRequestExecutionQueueProvider.cs @@ -15,11 +15,11 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript; [ExportStatelessLspService(typeof(IRequestExecutionQueueProvider), ProtocolConstants.TypeScriptLanguageContract), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, true)] -internal sealed class VSTypeScriptRequestExecutionQueueProvider(IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider) : IRequestExecutionQueueProvider +internal sealed class VSTypeScriptRequestExecutionQueueProvider() : IRequestExecutionQueueProvider { public IRequestExecutionQueue CreateRequestExecutionQueue(AbstractLanguageServer languageServer, ILspLogger logger, AbstractHandlerProvider handlerProvider) { - var queue = new RoslynRequestExecutionQueue(languageServer, logger, handlerProvider, asynchronousOperationListenerProvider); + var queue = new RoslynRequestExecutionQueue(languageServer, logger, handlerProvider); queue.Start(); return queue; } diff --git a/src/EditorFeatures/Core/InlineHints/InlineHintDataTag.cs b/src/EditorFeatures/Core/InlineHints/InlineHintDataTag.cs index ba9b5c57ac248..8607f14677567 100644 --- a/src/EditorFeatures/Core/InlineHints/InlineHintDataTag.cs +++ b/src/EditorFeatures/Core/InlineHints/InlineHintDataTag.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using Microsoft.CodeAnalysis.InlineHints; +using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Tagging; using Roslyn.Utilities; @@ -48,12 +49,12 @@ public bool Equals(InlineHintDataTag? other) return false; // Ensure both hints are talking about the same snapshot. - if (!_provider.SpanEquals(_snapshot, this.Hint.Span, other._snapshot, other.Hint.Span)) + if (!_provider.SpanEquals(this.Hint.Span.ToSnapshotSpan(_snapshot), other.Hint.Span.ToSnapshotSpan(other._snapshot))) return false; if (this.Hint.ReplacementTextChange != null && other.Hint.ReplacementTextChange != null && - !_provider.SpanEquals(_snapshot, this.Hint.ReplacementTextChange.Value.Span, other._snapshot, other.Hint.ReplacementTextChange.Value.Span)) + !_provider.SpanEquals(this.Hint.ReplacementTextChange.Value.Span.ToSnapshotSpan(_snapshot), other.Hint.ReplacementTextChange.Value.Span.ToSnapshotSpan(other._snapshot))) { return false; } diff --git a/src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs b/src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs index 76f1ab0e43683..43cdbfe4190bf 100644 --- a/src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs +++ b/src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs @@ -12,14 +12,12 @@ using Microsoft.CodeAnalysis.Editor.Tagging; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.InlineHints; -using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; -using Roslyn.Utilities; using VSUtilities = Microsoft.VisualStudio.Utilities; namespace Microsoft.CodeAnalysis.Editor.InlineHints; @@ -33,10 +31,10 @@ namespace Microsoft.CodeAnalysis.Editor.InlineHints; [VSUtilities.Name(nameof(InlineHintsDataTaggerProvider))] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] [method: ImportingConstructor] -internal partial class InlineHintsDataTaggerProvider( +internal sealed partial class InlineHintsDataTaggerProvider( TaggerHost taggerHost, [Import(AllowDefault = true)] IInlineHintKeyProcessor inlineHintKeyProcessor) - : AsynchronousViewTaggerProvider(taggerHost, FeatureAttribute.InlineHints) + : AsynchronousViewportTaggerProvider(taggerHost, FeatureAttribute.InlineHints) { private readonly IInlineHintKeyProcessor _inlineHintKeyProcessor = inlineHintKeyProcessor; @@ -74,28 +72,12 @@ protected override ITaggerEventSource CreateEventSource(ITextView textView, ITex option.Equals(InlineHintsOptionsStorage.ForCollectionExpressions))); } - protected override void AddSpansToTag(ITextView? textView, ITextBuffer subjectBuffer, ref TemporaryArray result) - { - this.ThreadingContext.ThrowIfNotOnUIThread(); - Contract.ThrowIfNull(textView); - - // Find the visible span some 100 lines +/- what's actually in view. This way - // if the user scrolls up/down, we'll already have the results. - var visibleSpanOpt = textView.GetVisibleLinesSpan(subjectBuffer, extraLines: 100); - if (visibleSpanOpt == null) - { - // Couldn't find anything visible, just fall back to tagging all hint locations - base.AddSpansToTag(textView, subjectBuffer, ref result); - return; - } - - result.Add(visibleSpanOpt.Value); - } - protected override async Task ProduceTagsAsync( - TaggerContext context, DocumentSnapshotSpan documentSnapshotSpan, int? caretPosition, CancellationToken cancellationToken) + TaggerContext context, + DocumentSnapshotSpan spanToTag, + CancellationToken cancellationToken) { - var document = documentSnapshotSpan.Document; + var document = spanToTag.Document; if (document == null) return; @@ -111,7 +93,7 @@ protected override async Task ProduceTagsAsync( var options = GlobalOptions.GetInlineHintsOptions(document.Project.Language); - var snapshotSpan = documentSnapshotSpan.SnapshotSpan; + var snapshotSpan = spanToTag.SnapshotSpan; var hints = await service.GetInlineHintsAsync( document, snapshotSpan.Span.ToTextSpan(), options, displayAllOverride: _inlineHintKeyProcessor?.State is true, diff --git a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs index 23542684ff4b8..17a56e095c14d 100644 --- a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs +++ b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs @@ -754,9 +754,7 @@ private static bool TryGetInitialTriggerLocation(AsyncCompletionSessionDataSnaps return false; } - private bool IsHardSelection( - RoslynCompletionItem item, - bool matchedFilterText) + private bool IsHardSelection(RoslynCompletionItem item, bool matchedFilterText) { if (_hasSuggestedItemOptions) { @@ -779,7 +777,7 @@ private bool IsHardSelection( // It's possible the user is just typing language punctuation and selecting // anything in the list will interfere. We only allow this if the filter text // exactly matches something in the list already. - if (_filterText.Length > 0 && IsAllPunctuation(_filterText) && _filterText != item.DisplayText) + if (_filterText.Length > 0 && CompletionService.IsAllPunctuation(_filterText) && _filterText != item.DisplayText) { return false; } @@ -819,19 +817,6 @@ private bool IsHardSelection( return true; } - private static bool IsAllPunctuation(string filterText) - { - foreach (var ch in filterText) - { - if (!char.IsPunctuation(ch)) - { - return false; - } - } - - return true; - } - /// /// A potential filter character is something that can filter a completion lists and is /// *guaranteed* to not be a commit character. diff --git a/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs b/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs index 4b57b1206c487..ceb74bae26bfb 100644 --- a/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/ReferenceHighlighting/ReferenceHighlightingViewTaggerProvider.cs @@ -77,12 +77,16 @@ protected override ITaggerEventSource CreateEventSource(ITextView textView, ITex return textViewOpt.BufferGraph.MapDownToFirstMatch(textViewOpt.Selection.Start.Position, PointTrackingMode.Positive, b => IsSupportedContentType(b.ContentType), PositionAffinity.Successor); } - protected override void AddSpansToTag(ITextView textViewOpt, ITextBuffer subjectBuffer, ref TemporaryArray result) + protected override bool TryAddSpansToTag(ITextView textViewOpt, ITextBuffer subjectBuffer, ref TemporaryArray result) { // Note: this may return no snapshot spans. We have to be resilient to that // when processing the TaggerContext<>.SpansToTag below. foreach (var buffer in textViewOpt.BufferGraph.GetTextBuffers(b => IsSupportedContentType(b.ContentType))) result.Add(buffer.CurrentSnapshot.GetFullSpan()); + + // Fine to always return 'true' here. If we get no spans to tag, we can move ourselves to the no-tags state and + // update the editor. + return true; } protected override Task ProduceTagsAsync( diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index 9c288cc54b153..6f1b14d5dc446 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -3,13 +3,16 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Notification; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; @@ -48,7 +51,7 @@ internal sealed class SolutionChecksumUpdater private readonly AsyncBatchingWorkQueue _synchronizeActiveDocumentQueue; private readonly object _gate = new(); - private bool _isPaused; + private bool _isSynchronizeWorkspacePaused; public SolutionChecksumUpdater( Workspace workspace, @@ -62,20 +65,22 @@ public SolutionChecksumUpdater( _workspace = workspace; _documentTrackingService = workspace.Services.GetRequiredService(); - _textChangeQueue = new AsyncBatchingWorkQueue<(Document oldDocument, Document newDocument)>( + _synchronizeWorkspaceQueue = new AsyncBatchingWorkQueue( DelayTimeSpan.NearImmediate, - SynchronizeTextChangesAsync, + SynchronizePrimaryWorkspaceAsync, listener, shutdownToken); - _synchronizeWorkspaceQueue = new AsyncBatchingWorkQueue( - DelayTimeSpan.NearImmediate, - SynchronizePrimaryWorkspaceAsync, + // Text changes and active doc info are tiny messages. So attempt to send them immediately. Just batching + // things up if we get a flurry of notifications. + _textChangeQueue = new AsyncBatchingWorkQueue<(Document oldDocument, Document newDocument)>( + TimeSpan.Zero, + SynchronizeTextChangesAsync, listener, shutdownToken); _synchronizeActiveDocumentQueue = new AsyncBatchingWorkQueue( - DelayTimeSpan.NearImmediate, + TimeSpan.Zero, SynchronizeActiveDocumentAsync, listener, shutdownToken); @@ -90,14 +95,15 @@ public SolutionChecksumUpdater( _globalOperationService.Stopped += OnGlobalOperationStopped; } - // Enqueue the work to sync the initial solution. - ResumeWork(); + // Enqueue the work to sync the initial data over. + _synchronizeActiveDocumentQueue.AddWork(); + _synchronizeWorkspaceQueue.AddWork(); } public void Shutdown() { // Try to stop any work that is in progress. - PauseWork(); + PauseSynchronizingPrimaryWorkspace(); _documentTrackingService.ActiveDocumentChanged -= OnActiveDocumentChanged; _workspace.WorkspaceChanged -= OnWorkspaceChanged; @@ -110,43 +116,33 @@ public void Shutdown() } private void OnGlobalOperationStarted(object? sender, EventArgs e) - => PauseWork(); + => PauseSynchronizingPrimaryWorkspace(); private void OnGlobalOperationStopped(object? sender, EventArgs e) - => ResumeWork(); + => ResumeSynchronizingPrimaryWorkspace(); - private void PauseWork() + private void PauseSynchronizingPrimaryWorkspace() { // An expensive global operation started (like a build). Pause ourselves and cancel any outstanding work in // progress to synchronize the solution. lock (_gate) { _synchronizeWorkspaceQueue.CancelExistingWork(); - _synchronizeActiveDocumentQueue.CancelExistingWork(); - _isPaused = true; + _isSynchronizeWorkspacePaused = true; } } - private void ResumeWork() + private void ResumeSynchronizingPrimaryWorkspace() { lock (_gate) { - _isPaused = false; - _synchronizeActiveDocumentQueue.AddWork(); + _isSynchronizeWorkspacePaused = false; _synchronizeWorkspaceQueue.AddWork(); } } private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) { - // Check if we're currently paused. If so ignore this notification. We don't want to any work in response - // to whatever the workspace is doing. - lock (_gate) - { - if (_isPaused) - return; - } - if (e.Kind == WorkspaceChangeKind.DocumentChanged) { var oldDocument = e.OldSolution.GetDocument(e.DocumentId); @@ -155,7 +151,13 @@ private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) _textChangeQueue.AddWork((oldDocument, newDocument)); } - _synchronizeWorkspaceQueue.AddWork(); + // Check if we're currently paused. If so ignore this notification. We don't want to any work in response + // to whatever the workspace is doing. + lock (_gate) + { + if (!_isSynchronizeWorkspacePaused) + _synchronizeWorkspaceQueue.AddWork(); + } } private void OnActiveDocumentChanged(object? sender, DocumentId? e) @@ -204,55 +206,53 @@ private async ValueTask SynchronizeTextChangesAsync( ImmutableSegmentedList<(Document oldDocument, Document newDocument)> values, CancellationToken cancellationToken) { - foreach (var (oldDocument, newDocument) in values) - { - cancellationToken.ThrowIfCancellationRequested(); - await SynchronizeTextChangesAsync(oldDocument, newDocument, cancellationToken).ConfigureAwait(false); - } + var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false); + if (client == null) + return; - return; + // this pushes text changes to the remote side if it can. this is purely perf optimization. whether this + // pushing text change worked or not doesn't affect feature's functionality. + // + // this basically see whether it can cheaply find out text changes between 2 snapshots, if it can, it will + // send out that text changes to remote side. + // + // the remote side, once got the text change, will again see whether it can use that text change information + // without any high cost and create new snapshot from it. + // + // otherwise, it will do the normal behavior of getting full text from VS side. this optimization saves + // times we need to do full text synchronization for typing scenario. + using var _ = ArrayBuilder<(DocumentId id, Checksum textChecksum, ImmutableArray changes, Checksum newTextChecksum)>.GetInstance(out var builder); - async ValueTask SynchronizeTextChangesAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken) + foreach (var (oldDocument, newDocument) in values) { - // this pushes text changes to the remote side if it can. this is purely perf optimization. whether this - // pushing text change worked or not doesn't affect feature's functionality. - // - // this basically see whether it can cheaply find out text changes between 2 snapshots, if it can, it will - // send out that text changes to remote side. - // - // the remote side, once got the text change, will again see whether it can use that text change information - // without any high cost and create new snapshot from it. - // - // otherwise, it will do the normal behavior of getting full text from VS side. this optimization saves - // times we need to do full text synchronization for typing scenario. - if (!oldDocument.TryGetText(out var oldText) || !newDocument.TryGetText(out var newText)) { // we only support case where text already exist - return; + continue; } // Avoid allocating text before seeing if we can bail out. var changeRanges = newText.GetChangeRanges(oldText).AsImmutable(); if (changeRanges.Length == 0) - return; + continue; // no benefit here. pulling from remote host is more efficient if (changeRanges is [{ Span.Length: var singleChangeLength }] && singleChangeLength == oldText.Length) - return; - - var textChanges = newText.GetTextChanges(oldText).AsImmutable(); - - var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false); - if (client == null) - return; + continue; var state = await oldDocument.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + var newState = await newDocument.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); - await client.TryInvokeAsync( - (service, cancellationToken) => service.SynchronizeTextAsync(oldDocument.Id, state.Text, textChanges, cancellationToken), - cancellationToken).ConfigureAwait(false); + var textChanges = newText.GetTextChanges(oldText).AsImmutable(); + builder.Add((oldDocument.Id, state.Text, textChanges, newState.Text)); } + + if (builder.Count == 0) + return; + + await client.TryInvokeAsync( + (service, cancellationToken) => service.SynchronizeTextChangesAsync(builder.ToImmutableAndClear(), cancellationToken), + cancellationToken).ConfigureAwait(false); } } diff --git a/src/EditorFeatures/Core/Shared/Extensions/ITextViewExtensions.cs b/src/EditorFeatures/Core/Shared/Extensions/ITextViewExtensions.cs index 8eb610af62de6..8c5027115121d 100644 --- a/src/EditorFeatures/Core/Shared/Extensions/ITextViewExtensions.cs +++ b/src/EditorFeatures/Core/Shared/Extensions/ITextViewExtensions.cs @@ -320,61 +320,6 @@ public static bool TryGetSurfaceBufferSpan( return false; } - /// - /// Returns the span of the lines in subjectBuffer that is currently visible in the provided - /// view. "extraLines" can be provided to get a span that encompasses some number of lines - /// before and after the actual visible lines. - /// - public static SnapshotSpan? GetVisibleLinesSpan(this ITextView textView, ITextBuffer subjectBuffer, int extraLines = 0) - { - // No point in continuing if the text view has been closed. - if (textView.IsClosed) - { - return null; - } - - // If we're being called while the textview is actually in the middle of a layout, then - // we can't proceed. Much of the text view state is unsafe to access (and will throw). - if (textView.InLayout) - { - return null; - } - - // During text view initialization the TextViewLines may be null. In that case we can't - // get an appropriate visisble span. - if (textView.TextViewLines == null) - { - return null; - } - - // Determine the range of text that is visible in the view. Then map this down to the - // bufffer passed in. From that, determine the start/end line for the buffer that is in - // view. - var visibleSpan = textView.TextViewLines.FormattedSpan; - var visibleSpansInBuffer = textView.BufferGraph.MapDownToBuffer(visibleSpan, SpanTrackingMode.EdgeInclusive, subjectBuffer); - if (visibleSpansInBuffer.Count == 0) - { - return null; - } - - var visibleStart = visibleSpansInBuffer.First().Start; - var visibleEnd = visibleSpansInBuffer.Last().End; - - var snapshot = subjectBuffer.CurrentSnapshot; - var startLine = visibleStart.GetContainingLineNumber(); - var endLine = visibleEnd.GetContainingLineNumber(); - - startLine = Math.Max(startLine - extraLines, 0); - endLine = Math.Min(endLine + extraLines, snapshot.LineCount - 1); - - var start = snapshot.GetLineFromLineNumber(startLine).Start; - var end = snapshot.GetLineFromLineNumber(endLine).EndIncludingLineBreak; - - var span = new SnapshotSpan(snapshot, Span.FromBounds(start, end)); - - return span; - } - /// /// Determines if the textbuffer passed in matches the buffer for the textview. /// diff --git a/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.cs b/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.cs index 85948abd1fd45..5ca068091ef55 100644 --- a/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.cs +++ b/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.cs @@ -118,6 +118,14 @@ public void AddAllSpans(ITextSnapshot textSnapshot, SegmentedList> tagSpans.Add(GetTranslatedTagSpan(tagSpan, textSnapshot)); } + /// + /// Spans will be added in sorted order + public void AddAllSpans(ITextSnapshot textSnapshot, SegmentedList<(SnapshotSpan, TTag)> tagSpans) + { + foreach (var tagSpan in _tree) + tagSpans.Add((GetTranslatedSpan(tagSpan, textSnapshot, _spanTrackingMode), tagSpan.Tag)); + } + /// /// Removes from all the tags spans in that intersect with any of /// the spans in . diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs index 0cc95245b81f2..baa6b50ad5e03 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs @@ -294,9 +294,19 @@ private async ValueTask ProcessEventChangeAsync( // us avoid hammering the dispatcher queue with lots of work that causes contention. Additionally, use // a no-throw awaitable so that in the common case where we cancel before, we don't throw an exception // that can exacerbate cross process debugging scenarios. - var (isVisible, caretPosition, snapshotSpansToTag) = await _dataSource.MainThreadManager.PerformWorkOnMainThreadAsync( + var valueOpt = await _dataSource.MainThreadManager.PerformWorkOnMainThreadAsync( GetTaggerUIData, cancellationToken).ConfigureAwait(true); + if (valueOpt is null) + { + // We failed to get the UI data we need. This can happen in cases like trying to get that during a layout pass. + // In that case, we just bail out and try again later. + this.EnqueueWork(highPriority); + return null; + } + + var (isVisible, caretPosition, snapshotSpansToTag) = valueOpt.Value; + // Since we don't ever throw above, check and see if the await completed due to cancellation and do not // proceed. if (cancellationToken.IsCancellationRequested) @@ -379,7 +389,9 @@ static async (oldTagTrees, args, cancellationToken) => return newTagTrees; } - (bool isVisible, SnapshotPoint? caretPosition, OneOrMany spansToTag) GetTaggerUIData() + // Returns null if we we're currently in a state where we can't get the tags to span and we should bail out + // from producing tags on this turn of the crank. + (bool isVisible, SnapshotPoint? caretPosition, OneOrMany spansToTag)? GetTaggerUIData() { _dataSource.ThreadingContext.ThrowIfNotOnUIThread(); @@ -393,7 +405,8 @@ static async (oldTagTrees, args, cancellationToken) => var caretPosition = _dataSource.GetCaretPoint(_textView, _subjectBuffer); using var spansToTag = TemporaryArray.Empty; - _dataSource.AddSpansToTag(_textView, _subjectBuffer, ref spansToTag.AsRef()); + if (!_dataSource.TryAddSpansToTag(_textView, _subjectBuffer, ref spansToTag.AsRef())) + return null; #if DEBUG foreach (var snapshotSpan in spansToTag) @@ -629,8 +642,8 @@ private DiffResult ComputeDifference( TagSpanIntervalTree latestTree, TagSpanIntervalTree previousTree) { - using var _1 = SegmentedListPool.GetPooledList>(out var latestSpans); - using var _2 = SegmentedListPool.GetPooledList>(out var previousSpans); + using var _1 = SegmentedListPool.GetPooledList<(SnapshotSpan, TTag)>(out var latestSpans); + using var _2 = SegmentedListPool.GetPooledList<(SnapshotSpan, TTag)>(out var previousSpans); using var _3 = ArrayBuilder.GetInstance(out var added); using var _4 = ArrayBuilder.GetInstance(out var removed); @@ -646,8 +659,8 @@ private DiffResult ComputeDifference( while (latest != null && previous != null) { - var latestSpan = latest.Span; - var previousSpan = previous.Span; + var latestSpan = latest.Value.Span; + var previousSpan = previous.Value.Span; if (latestSpan.Start < previousSpan.Start) { @@ -675,7 +688,7 @@ private DiffResult ComputeDifference( } else { - if (!_dataSource.TagEquals(latest.Tag, previous.Tag)) + if (!_dataSource.TagEquals(latest.Value.Tag, previous.Value.Tag)) added.Add(latestSpan); latest = NextOrNull(ref latestEnumerator); @@ -686,19 +699,19 @@ private DiffResult ComputeDifference( while (latest != null) { - added.Add(latest.Span); + added.Add(latest.Value.Span); latest = NextOrNull(ref latestEnumerator); } while (previous != null) { - removed.Add(previous.Span); + removed.Add(previous.Value.Span); previous = NextOrNull(ref previousEnumerator); } return new DiffResult(new(added), new(removed)); - static TagSpan? NextOrNull(ref SegmentedList>.Enumerator enumerator) + static (SnapshotSpan Span, TTag Tag)? NextOrNull(ref SegmentedList<(SnapshotSpan, TTag)>.Enumerator enumerator) => enumerator.MoveNext() ? enumerator.Current : null; } diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs index 43881d20097b2..537f91ff1ed35 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs @@ -207,19 +207,29 @@ private void StoreTagSource(ITextView? textView, ITextBuffer subjectBuffer, TagS => textView?.GetCaretPoint(subjectBuffer); /// - /// Called by the infrastructure to determine - /// the set of spans that it should asynchronously tag. This will be called in response to - /// notifications from the that something has changed, and - /// will only be called from the UI thread. The tagger infrastructure will then determine - /// the s associated with these s - /// and will asynchronously call into at some point in - /// the future to produce tags for these spans. + /// Called by the infrastructure to determine the set of + /// spans that it should asynchronously tag. This will be called in response to notifications from the that something has changed, and will only be called from the UI thread. The tagger + /// infrastructure will then determine the s associated with these s and will asynchronously call into at some point in the future to produce tags for these spans. /// - protected virtual void AddSpansToTag( + /// if spans could not be added and if tagging should abort and re-run at a later + /// point. Note: is not equivalent to along with no spans returned. + /// The latter means we can proceed to actual tag computation, just that since there are no applicable spans, we + /// should produce no tags whatsoever. The former means 'we cannot even proceed to tagging', 'we should preserve + /// whatever tags we current have', and 'we should rerun tagging in the future to see if we can proceed'. Examples + /// of where should be used include if the needs to be queried + /// for data, but it is in a state where it cannot respond to queries (for example, it is in the process of a + /// layout). In that case, we cannot proceed, and we do not want to clear existing tags. Instead, we want to wait + /// a short while till the next applicable time to tag. + /// + protected virtual bool TryAddSpansToTag( ITextView? textView, ITextBuffer subjectBuffer, ref TemporaryArray result) { // For a standard tagger, the spans to tag is the span of the entire snapshot. result.Add(subjectBuffer.CurrentSnapshot.GetFullSpan()); + return true; } /// diff --git a/src/EditorFeatures/Core/Tagging/AsynchronousViewportTaggerProvider.SingleViewportTaggerProvider.cs b/src/EditorFeatures/Core/Tagging/AsynchronousViewportTaggerProvider.SingleViewportTaggerProvider.cs index 4f0c94b6abcd2..4b2b118f431de 100644 --- a/src/EditorFeatures/Core/Tagging/AsynchronousViewportTaggerProvider.SingleViewportTaggerProvider.cs +++ b/src/EditorFeatures/Core/Tagging/AsynchronousViewportTaggerProvider.SingleViewportTaggerProvider.cs @@ -2,8 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; @@ -62,23 +64,67 @@ protected override bool CancelOnNewWork // This can save a lot of CPU time for things that may never even be looked at. => _viewPortToTag != ViewPortToTag.InView; - protected override void AddSpansToTag(ITextView? textView, ITextBuffer subjectBuffer, ref TemporaryArray result) + /// + /// Returns the span of the lines in subjectBuffer that is currently visible in the provided + /// view. "extraLines" can be provided to get a span that encompasses some number of lines + /// before and after the actual visible lines. + /// + private static SnapshotSpan? GetVisibleLinesSpan(ITextView textView, ITextBuffer subjectBuffer, int extraLines) + { + // Determine the range of text that is visible in the view. Then map this down to the buffer passed in. From + // that, determine the start/end line for the buffer that is in view. + var visibleSpan = textView.TextViewLines.FormattedSpan; + var visibleSpansInBuffer = textView.BufferGraph.MapDownToBuffer(visibleSpan, SpanTrackingMode.EdgeInclusive, subjectBuffer); + if (visibleSpansInBuffer.Count == 0) + return null; + + var visibleStart = visibleSpansInBuffer.First().Start; + var visibleEnd = visibleSpansInBuffer.Last().End; + + var snapshot = subjectBuffer.CurrentSnapshot; + var startLine = visibleStart.GetContainingLineNumber(); + var endLine = visibleEnd.GetContainingLineNumber(); + + startLine = Math.Max(startLine - extraLines, 0); + endLine = Math.Min(endLine + extraLines, snapshot.LineCount - 1); + + var start = snapshot.GetLineFromLineNumber(startLine).Start; + var end = snapshot.GetLineFromLineNumber(endLine).EndIncludingLineBreak; + + var span = new SnapshotSpan(snapshot, Span.FromBounds(start, end)); + + return span; + } + + protected override bool TryAddSpansToTag(ITextView? textView, ITextBuffer subjectBuffer, ref TemporaryArray result) { this.ThreadingContext.ThrowIfNotOnUIThread(); Contract.ThrowIfNull(textView); + // View is closed. Return no spans so we can remove all tags. + if (textView.IsClosed) + return true; + + // If we're in a layout, then we can't even determine what our visible span is. Bail out immediately as qe + // don't want to suddenly flip to tagging everything, then go back to tagging a small subset of the view + // afterwards. + // + // In this case we literally do not know what is visible, so we want to bail and try again later. + if (textView.InLayout) + return false; + + // During text view initialization the TextViewLines may be null. In that case nothing is really visible. + // So return no spans so we can remove all tags. + if (textView.TextViewLines == null) + return true; + // if we're the current view, attempt to just get what's visible, plus 10 lines above and below. This will // ensure that moving up/down a few lines tends to have immediate accurate results. - var visibleSpanOpt = textView.GetVisibleLinesSpan(subjectBuffer, extraLines: s_standardLineCountAroundViewportToTag); - if (visibleSpanOpt is null) - { - // couldn't figure out the visible span. So the InView tagger will need to tag everything, and the - // above/below tagger should tag nothing. - if (_viewPortToTag == ViewPortToTag.InView) - base.AddSpansToTag(textView, subjectBuffer, ref result); + var visibleSpanOpt = GetVisibleLinesSpan(textView, subjectBuffer, extraLines: s_standardLineCountAroundViewportToTag); - return; - } + // Nothing was visible at all. Return no spans so we can remove all tags. + if (visibleSpanOpt is null) + return true; var visibleSpan = visibleSpanOpt.Value; @@ -86,29 +132,34 @@ protected override void AddSpansToTag(ITextView? textView, ITextBuffer subjectBu if (_viewPortToTag is ViewPortToTag.InView) { result.Add(visibleSpan); - return; } - - // For the above/below tagger, broaden the span to to the requested portion above/below what's visible, then - // subtract out the visible range. - var widenedSpanOpt = textView.GetVisibleLinesSpan(subjectBuffer, extraLines: _callback._extraLinesAroundViewportToTag); - Contract.ThrowIfNull(widenedSpanOpt, "Should not ever fail getting the widened span as we were able to get the normal visible span"); - - var widenedSpan = widenedSpanOpt.Value; - Contract.ThrowIfFalse(widenedSpan.Span.Contains(visibleSpan.Span), "The widened span must be at least as large as the visible one."); - - if (_viewPortToTag is ViewPortToTag.Above) + else { - var aboveSpan = new SnapshotSpan(visibleSpan.Snapshot, Span.FromBounds(widenedSpan.Span.Start, visibleSpan.Span.Start)); - if (!aboveSpan.IsEmpty) - result.Add(aboveSpan); - } - else if (_viewPortToTag is ViewPortToTag.Below) - { - var belowSpan = new SnapshotSpan(visibleSpan.Snapshot, Span.FromBounds(visibleSpan.Span.End, widenedSpan.Span.End)); - if (!belowSpan.IsEmpty) - result.Add(belowSpan); + // For the above/below tagger, broaden the span to to the requested portion above/below what's visible, then + // subtract out the visible range. + var widenedSpanOpt = GetVisibleLinesSpan(textView, subjectBuffer, extraLines: _callback._extraLinesAroundViewportToTag); + Contract.ThrowIfNull(widenedSpanOpt, "Should not ever fail getting the widened span as we were able to get the normal visible span"); + + var widenedSpan = widenedSpanOpt.Value; + Contract.ThrowIfFalse(widenedSpan.Span.Contains(visibleSpan.Span), "The widened span must be at least as large as the visible one."); + + if (_viewPortToTag is ViewPortToTag.Above) + { + var aboveSpan = new SnapshotSpan(visibleSpan.Snapshot, Span.FromBounds(widenedSpan.Span.Start, visibleSpan.Span.Start)); + if (!aboveSpan.IsEmpty) + result.Add(aboveSpan); + } + else if (_viewPortToTag is ViewPortToTag.Below) + { + var belowSpan = new SnapshotSpan(visibleSpan.Snapshot, Span.FromBounds(visibleSpan.Span.End, widenedSpan.Span.End)); + if (!belowSpan.IsEmpty) + result.Add(belowSpan); + } } + + // Unilaterally return true here, even if we determine we don't have a span to tag. In this case, we've + // computed that there really is nothing visible, in which case we *do* want to move to having no tags. + return true; } protected override async Task ProduceTagsAsync( diff --git a/src/EditorFeatures/Core/Tagging/EfficientTagger.cs b/src/EditorFeatures/Core/Tagging/EfficientTagger.cs index 8349cb739bfa7..af187baaba78b 100644 --- a/src/EditorFeatures/Core/Tagging/EfficientTagger.cs +++ b/src/EditorFeatures/Core/Tagging/EfficientTagger.cs @@ -30,12 +30,20 @@ IEnumerable> ITagger.GetTags(NormalizedSnapshotSpanCollecti => GetTags(spans); /// - /// Default impl of the core interface. Forces an allocation. + /// Default impl of the core interface. /// - public IReadOnlyList> GetTags(NormalizedSnapshotSpanCollection spans) - => SegmentedListPool>.ComputeList( - static (args, tags) => args.@this.AddTags(args.spans, tags), - (@this: this, spans)); + public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) + { + using var pooledObject = SegmentedListPool.GetPooledList>(out var list); + + AddTags(spans, list); + + // Use yield return mechanism to allow the segmented list to get returned back to the + // pool after usage. This does cause an allocation for the yield state machinery, but + // that is better than not freeing a potentially large segmented list back to the pool. + foreach (var item in list) + yield return item; + } public virtual event EventHandler? TagsChanged; diff --git a/src/EditorFeatures/Core/Tagging/TaggerMainThreadManager.cs b/src/EditorFeatures/Core/Tagging/TaggerMainThreadManager.cs index e0c8e958b5508..5378232757c2d 100644 --- a/src/EditorFeatures/Core/Tagging/TaggerMainThreadManager.cs +++ b/src/EditorFeatures/Core/Tagging/TaggerMainThreadManager.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.Tagging; -using QueueData = (Func action, TaskCompletionSource taskCompletionSource, CancellationToken cancellationToken); +using QueueData = (Func action, TaskCompletionSource taskCompletionSource, CancellationToken cancellationToken); internal sealed class TaggerMainThreadManager { @@ -36,8 +36,8 @@ public TaggerMainThreadManager( /// This will not ever throw. private static void RunActionAndUpdateCompletionSource_NoThrow( - Func action, - TaskCompletionSource taskCompletionSource, + Func action, + TaskCompletionSource taskCompletionSource, CancellationToken cancellationToken) { try @@ -60,7 +60,7 @@ private static void RunActionAndUpdateCompletionSource_NoThrow( } finally { - taskCompletionSource.TrySetResult(default); + taskCompletionSource.TrySetResult(null); Contract.ThrowIfFalse(taskCompletionSource.Task.IsCompleted); } } @@ -69,9 +69,9 @@ private static void RunActionAndUpdateCompletionSource_NoThrow( /// Adds the provided action to a queue that will run on the UI thread in the near future (batched with other /// registered actions). If the cancellation token is triggered before the action runs, it will not be run. /// - public async ValueTask PerformWorkOnMainThreadAsync(Func action, CancellationToken cancellationToken) + public async ValueTask PerformWorkOnMainThreadAsync(Func action, CancellationToken cancellationToken) { - var taskSource = new TaskCompletionSource(); + var taskSource = new TaskCompletionSource(); // If we're already on the main thread, just run the action directly without any delay. This is important // for cases where the tagger is performing a blocking call to get tags synchronously on the UI thread (for @@ -84,7 +84,7 @@ public async ValueTask PerformWorkOnMainThreadAsync(Func ((TaskCompletionSource)taskSourceObj!).TrySetCanceled(), taskSource); + static taskSourceObj => ((TaskCompletionSource)taskSourceObj!).TrySetCanceled(), taskSource); _workQueue.AddWork((action, taskSource, cancellationToken)); diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_GenerateTypeDialog.cs b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_GenerateTypeDialog.cs index 712b5060e3b07..45605d118ff13 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_GenerateTypeDialog.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_GenerateTypeDialog.cs @@ -167,9 +167,9 @@ await TestOperationsAsync(testState.Workspace, expectedTextWithUsings, operation if (assertGenerateTypeDialogOptions != null) { - Assert.True(assertGenerateTypeDialogOptions.IsPublicOnlyAccessibility == generateTypeDialogOptions.IsPublicOnlyAccessibility); - Assert.True(assertGenerateTypeDialogOptions.TypeKindOptions == generateTypeDialogOptions.TypeKindOptions); - Assert.True(assertGenerateTypeDialogOptions.IsAttribute == generateTypeDialogOptions.IsAttribute); + Assert.Equal(assertGenerateTypeDialogOptions.IsPublicOnlyAccessibility, generateTypeDialogOptions.IsPublicOnlyAccessibility); + Assert.Equal(assertGenerateTypeDialogOptions.TypeKindOptions, generateTypeDialogOptions.TypeKindOptions); + Assert.Equal(assertGenerateTypeDialogOptions.IsAttribute, generateTypeDialogOptions.IsAttribute); } if (assertTypeKindPresent != null) diff --git a/src/EditorFeatures/ExternalAccess/Debugger/GlassTestsHotReloadService.cs b/src/EditorFeatures/ExternalAccess/Debugger/GlassTestsHotReloadService.cs index 891067cc79258..8c21d74f0f2f6 100644 --- a/src/EditorFeatures/ExternalAccess/Debugger/GlassTestsHotReloadService.cs +++ b/src/EditorFeatures/ExternalAccess/Debugger/GlassTestsHotReloadService.cs @@ -84,8 +84,8 @@ public void EndDebuggingSession() public async ValueTask GetUpdatesAsync(Solution solution, CancellationToken cancellationToken) { - var results = (await _encService.EmitSolutionUpdateAsync(GetSessionId(), solution, s_noActiveStatementSpanProvider, cancellationToken).ConfigureAwait(false)).Dehydrate(); - return new ManagedHotReloadUpdates(results.ModuleUpdates.Updates.FromContract(), results.GetAllDiagnostics().FromContract()); + var results = (await _encService.EmitSolutionUpdateAsync(GetSessionId(), solution, runningProjects: [], s_noActiveStatementSpanProvider, cancellationToken).ConfigureAwait(false)).Dehydrate(); + return new ManagedHotReloadUpdates(results.ModuleUpdates.Updates.FromContract(), results.GetAllDiagnostics().FromContract(), [], []); } } } diff --git a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs index fb37145993f67..c676221e32194 100644 --- a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs @@ -478,12 +478,18 @@ public void CSharp_VerifyIDEDiagnosticSeveritiesAreConfigurable() # IDE0305 dotnet_diagnostic.IDE0305.severity = %value% + + # IDE0306 + dotnet_diagnostic.IDE0306.severity = %value% # IDE0320 dotnet_diagnostic.IDE0320.severity = %value% # IDE0330 dotnet_diagnostic.IDE0330.severity = %value% + + # IDE0340 + dotnet_diagnostic.IDE0340.severity = %value% # IDE1005 dotnet_diagnostic.IDE1005.severity = %value% @@ -898,8 +904,10 @@ public void CSharp_VerifyIDECodeStyleOptionsAreConfigurable() ("IDE0303", "dotnet_style_prefer_collection_expression", "when_types_loosely_match"), ("IDE0304", "dotnet_style_prefer_collection_expression", "when_types_loosely_match"), ("IDE0305", "dotnet_style_prefer_collection_expression", "when_types_loosely_match"), + ("IDE0306", "dotnet_style_prefer_collection_expression", "when_types_loosely_match"), ("IDE0320", "csharp_prefer_static_anonymous_function", "true"), ("IDE0330", "csharp_prefer_system_threading_lock", "true"), + ("IDE0340", "csharp_style_prefer_unbound_generic_type_in_nameof", "true"), ("IDE1005", "csharp_style_conditional_delegate_call", "true"), ("IDE1006", null, null), ("IDE1007", null, null), diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueLanguageServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueLanguageServiceTests.cs index 5d24bdf93285a..eaf50833afbf7 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueLanguageServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueLanguageServiceTests.cs @@ -156,7 +156,7 @@ await localWorkspace.ChangeSolutionAsync(localWorkspace.CurrentSolution var diagnosticDescriptor1 = EditAndContinueDiagnosticDescriptors.GetDescriptor(EditAndContinueErrorCode.ErrorReadingFile); - mockEncService.EmitSolutionUpdateImpl = (solution, _) => + mockEncService.EmitSolutionUpdateImpl = (solution, runningProjects, _) => { var syntaxTree = solution.GetRequiredDocument(documentId).GetSyntaxTreeSynchronously(CancellationToken.None)!; @@ -171,11 +171,13 @@ await localWorkspace.ChangeSolutionAsync(localWorkspace.CurrentSolution ModuleUpdates = new ModuleUpdates(ModuleUpdateStatus.Ready, []), Diagnostics = [new ProjectDiagnostics(project.Id, [documentDiagnostic, projectDiagnostic])], RudeEdits = [new ProjectDiagnostics(project.Id, [rudeEditDiagnostic])], - SyntaxError = syntaxError + SyntaxError = syntaxError, + ProjectsToRebuild = [project.Id], + ProjectsToRestart = [project.Id] }; }; - var updates = await localService.GetUpdatesAsync(CancellationToken.None); + var updates = await localService.GetUpdatesAsync(runningProjects: [project.FilePath], CancellationToken.None); Assert.Equal(++observedDiagnosticVersion, diagnosticRefresher.GlobalStateVersion); diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index 6639877600f7e..8ebab9e8a3091 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -229,6 +229,9 @@ public TestInheritanceMemberItem( MemberName = memberName; Targets = targets; } + + public override string ToString() + => MemberName; } private class TargetInfo @@ -248,7 +251,7 @@ public TargetInfo( string? projectName = null) { TargetSymbolDisplayName = targetSymbolDisplayName; - LocationTags = ImmutableArray.Create(locationTag); + LocationTags = [locationTag]; Relationship = relationship; LanguageGlyph = languageGlyph; InMetadata = false; @@ -375,18 +378,18 @@ public class Bar : IEnumerable var itemForBar = new TestInheritanceMemberItem( lineNumber: 3, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IEnumerable", relationship: InheritanceRelationship.ImplementedInterface, - inMetadata: true))); + inMetadata: true)]); var itemForGetEnumerator = new TestInheritanceMemberItem( lineNumber: 5, memberName: "IEnumerator Bar.GetEnumerator()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IEnumerable.GetEnumerator", relationship: InheritanceRelationship.ImplementedMember, - inMetadata: true))); + inMetadata: true)]); return VerifyInSingleDocumentAsync(markup, LanguageNames.CSharp, testHost, itemForBar, itemForGetEnumerator); } @@ -404,18 +407,18 @@ public class {|target2:Bar|} : IBar var itemOnLine2 = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemOnLine3 = new TestInheritanceMemberItem( lineNumber: 3, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); return VerifyInSingleDocumentAsync( markup, @@ -436,10 +439,10 @@ interface {|target2:IBar2|} : IBar { } var itemOnLine2 = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar2", locationTag: "target2", - relationship: InheritanceRelationship.ImplementingType)) + relationship: InheritanceRelationship.ImplementingType)] ); var itemOnLine3 = new TestInheritanceMemberItem( lineNumber: 3, @@ -470,18 +473,18 @@ class {|target1:B|} : A { } var itemOnLine2 = new TestInheritanceMemberItem( lineNumber: 2, memberName: "class A", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "B", locationTag: "target1", - relationship: InheritanceRelationship.DerivedType)) + relationship: InheritanceRelationship.DerivedType)] ); var itemOnLine3 = new TestInheritanceMemberItem( lineNumber: 3, memberName: "class B", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "A", locationTag: "target2", - relationship: InheritanceRelationship.BaseType)) + relationship: InheritanceRelationship.BaseType)] ); return VerifyInSingleDocumentAsync( @@ -535,11 +538,10 @@ public class {{|{SearchAreaTag}:Bar : Bar1 new TestInheritanceMemberItem( lineNumber: 4, memberName: "class Bar", - targets: ImmutableArray.Create( - new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar1", locationTag: "target1", - relationship: InheritanceRelationship.BaseType)))); + relationship: InheritanceRelationship.BaseType)])); } [Theory, CombinatorialData] @@ -561,34 +563,34 @@ public class {|target1:Bar|} : IBar var itemForIBar = new TestInheritanceMemberItem( lineNumber: 3, memberName: "interface IBar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 7, memberName: "class Bar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForEventInInterface = new TestInheritanceMemberItem( lineNumber: 5, memberName: "event EventHandler IBar.e", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar.e", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForEventInClass = new TestInheritanceMemberItem( lineNumber: 9, memberName: "event EventHandler Bar.e", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar.e", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -615,50 +617,50 @@ public class {|target1:Bar|} : IBar var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 6, memberName: "class Bar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForE1InInterface = new TestInheritanceMemberItem( lineNumber: 4, memberName: "event EventHandler IBar.e1", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar.e1", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForE2InInterface = new TestInheritanceMemberItem( lineNumber: 4, memberName: "event EventHandler IBar.e2", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar.e2", locationTag: "target4", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForE1InClass = new TestInheritanceMemberItem( lineNumber: 8, memberName: "event EventHandler Bar.e1", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar.e1", locationTag: "target5", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForE2InClass = new TestInheritanceMemberItem( lineNumber: 8, memberName: "event EventHandler Bar.e2", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar.e2", locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -693,91 +695,91 @@ public class {|target2:Bar|} : IBar var itemForEooInClass = new TestInheritanceMemberItem( lineNumber: 13, memberName: "event EventHandler Bar.Eoo", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Eoo", locationTag: "target8", - relationship: InheritanceRelationship.ImplementedMember)) + relationship: InheritanceRelationship.ImplementedMember)] ); var itemForEooInInterface = new TestInheritanceMemberItem( lineNumber: 6, memberName: "event EventHandler IBar.Eoo", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar.Eoo", locationTag: "target7", - relationship: InheritanceRelationship.ImplementingMember)) + relationship: InheritanceRelationship.ImplementingMember)] ); var itemForPooInInterface = new TestInheritanceMemberItem( lineNumber: 5, memberName: "int IBar.Poo { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar.Poo", locationTag: "target5", - relationship: InheritanceRelationship.ImplementingMember)) + relationship: InheritanceRelationship.ImplementingMember)] ); var itemForPooInClass = new TestInheritanceMemberItem( lineNumber: 12, memberName: "int Bar.Poo { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Poo", locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember)) + relationship: InheritanceRelationship.ImplementedMember)] ); var itemForFooInInterface = new TestInheritanceMemberItem( lineNumber: 4, memberName: "void IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar.Foo", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember)) + relationship: InheritanceRelationship.ImplementingMember)] ); var itemForFooInClass = new TestInheritanceMemberItem( lineNumber: 11, memberName: "void Bar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Foo", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedMember)) + relationship: InheritanceRelationship.ImplementedMember)] ); var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementingType)) + relationship: InheritanceRelationship.ImplementingType)] ); var itemForBar = new TestInheritanceMemberItem( lineNumber: 9, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementedInterface)) + relationship: InheritanceRelationship.ImplementedInterface)] ); var itemForIndexerInClass = new TestInheritanceMemberItem( lineNumber: 14, memberName: "int Bar.this[int] { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.this", locationTag: "target9", - relationship: InheritanceRelationship.ImplementedMember)) + relationship: InheritanceRelationship.ImplementedMember)] ); var itemForIndexerInInterface = new TestInheritanceMemberItem( lineNumber: 7, memberName: "int IBar.this[int] { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar.this", locationTag: "target10", - relationship: InheritanceRelationship.ImplementingMember)) + relationship: InheritanceRelationship.ImplementingMember)] ); return VerifyInSingleDocumentAsync( @@ -821,66 +823,66 @@ public class {{|target1:Bar2|}} : Bar var itemForEooInClass = new TestInheritanceMemberItem( lineNumber: 12, memberName: "override event EventHandler Bar2.Eoo", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: $"Bar.Eoo", locationTag: "target8", - relationship: InheritanceRelationship.OverriddenMember))); + relationship: InheritanceRelationship.OverriddenMember)]); var itemForEooInAbstractClass = new TestInheritanceMemberItem( lineNumber: 6, memberName: $"{modifier} event EventHandler Bar.Eoo", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar2.Eoo", locationTag: "target7", - relationship: InheritanceRelationship.OverridingMember))); + relationship: InheritanceRelationship.OverridingMember)]); var itemForPooInClass = new TestInheritanceMemberItem( lineNumber: 11, memberName: "override int Bar2.Poo { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: $"Bar.Poo", locationTag: "target6", - relationship: InheritanceRelationship.OverriddenMember))); + relationship: InheritanceRelationship.OverriddenMember)]); var itemForPooInAbstractClass = new TestInheritanceMemberItem( lineNumber: 5, memberName: $"{modifier} int Bar.Poo {{ get; set; }}", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar2.Poo", locationTag: "target5", - relationship: InheritanceRelationship.OverridingMember))); + relationship: InheritanceRelationship.OverridingMember)]); var itemForFooInAbstractClass = new TestInheritanceMemberItem( lineNumber: 4, memberName: $"{modifier} void Bar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar2.Foo", locationTag: "target3", - relationship: InheritanceRelationship.OverridingMember))); + relationship: InheritanceRelationship.OverridingMember)]); var itemForFooInClass = new TestInheritanceMemberItem( lineNumber: 10, memberName: "override void Bar2.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: $"Bar.Foo", locationTag: "target4", - relationship: InheritanceRelationship.OverriddenMember))); + relationship: InheritanceRelationship.OverriddenMember)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar2", locationTag: "target1", - relationship: InheritanceRelationship.DerivedType))); + relationship: InheritanceRelationship.DerivedType)]); var itemForBar2 = new TestInheritanceMemberItem( lineNumber: 8, memberName: "class Bar2", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target2", - relationship: InheritanceRelationship.BaseType))); + relationship: InheritanceRelationship.BaseType)]); return VerifyInSingleDocumentAsync( markup, @@ -930,75 +932,92 @@ public class {|target5:Bar2|} : Bar1, IBar var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Bar1", - locationTag: "target1", + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "Bar1", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType), + new TargetInfo( + targetSymbolDisplayName: "Bar2", + locationTag: "target5", relationship: InheritanceRelationship.ImplementingType), - new TargetInfo( - targetSymbolDisplayName: "Bar2", - locationTag: "target5", - relationship: InheritanceRelationship.ImplementingType))); + ]); var itemForFooInIBar = new TestInheritanceMemberItem( lineNumber: 4, memberName: "void IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Bar1.Foo", - locationTag: "target2", - relationship: InheritanceRelationship.ImplementingMember), + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "Bar1.Foo", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementingMember), new TargetInfo( targetSymbolDisplayName: "Bar2.Foo", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember), + ]); var itemForBar1 = new TestInheritanceMemberItem( lineNumber: 6, memberName: "class Bar1", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "IBar", - locationTag: "target4", - relationship: InheritanceRelationship.ImplementedInterface), + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "IBar", + locationTag: "target4", + relationship: InheritanceRelationship.ImplementedInterface), new TargetInfo( targetSymbolDisplayName: "Bar2", locationTag: "target5", - relationship: InheritanceRelationship.DerivedType))); + relationship: InheritanceRelationship.DerivedType), + ]); var itemForFooInBar1 = new TestInheritanceMemberItem( lineNumber: 8, memberName: "virtual void Bar1.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "IBar.Foo", - locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember), + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "IBar.Foo", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember), new TargetInfo( targetSymbolDisplayName: "Bar2.Foo", locationTag: "target3", - relationship: InheritanceRelationship.OverridingMember))); + relationship: InheritanceRelationship.OverridingMember), + ]); var itemForBar2 = new TestInheritanceMemberItem( lineNumber: 10, memberName: "class Bar2", - targets: ImmutableArray.Create( + targets: + [ new TargetInfo( - targetSymbolDisplayName: "Bar1", - locationTag: "target1", - relationship: InheritanceRelationship.BaseType), + targetSymbolDisplayName: "Bar1", + locationTag: "target1", + relationship: InheritanceRelationship.BaseType), new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface), + ]); var itemForFooInBar2 = new TestInheritanceMemberItem( lineNumber: 12, memberName: "override void Bar2.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "IBar.Foo", - locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember), + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "IBar.Foo", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember), new TargetInfo( targetSymbolDisplayName: "Bar1.Foo", locationTag: "target2", - relationship: InheritanceRelationship.OverriddenMember))); + relationship: InheritanceRelationship.OverriddenMember), + ]); return VerifyInSingleDocumentAsync( testDuplicate ? markup2 : markup1, @@ -1012,6 +1031,106 @@ public class {|target5:Bar2|} : Bar1, IBar itemForFooInBar2); } + [Theory] + [InlineData("abstract", TestHost.InProcess)] + [InlineData("abstract", TestHost.OutOfProcess)] + [InlineData("virtual", TestHost.InProcess)] + [InlineData("virtual", TestHost.OutOfProcess)] + public Task TestCSharpAbstractClassMembers_LooseMatch(string modifier, TestHost testHost) + { + var markup = $@"using System; + public abstract class {{|target2:Bar|}} + {{ + public {modifier} void {{|target4:Foo|}}(); + public {modifier} int {{|target6:Poo|}} {{ get; set; }} + public {modifier} event EventHandler {{|target8:Eoo|}}; + }} + public class {{|target1:Bar2|}} : Bar + {{ + public override void {{|target3:Foo|}}(int i) {{ }} + public override string {{|target5:Poo|}} {{ get; set; }} + public override event Action {{|target7:Eoo|}}; + }} + "; + + var itemForEooInClass = new TestInheritanceMemberItem( + lineNumber: 12, + memberName: "override event Action Bar2.Eoo", + targets: [new TargetInfo( + targetSymbolDisplayName: $"Bar.Eoo", + locationTag: "target8", + relationship: InheritanceRelationship.OverriddenMember)]); + + var itemForEooInAbstractClass = new TestInheritanceMemberItem( + lineNumber: 6, + memberName: $"{modifier} event EventHandler Bar.Eoo", + targets: [new TargetInfo( + targetSymbolDisplayName: "Bar2.Eoo", + locationTag: "target7", + relationship: InheritanceRelationship.OverridingMember)]); + + var itemForPooInClass = new TestInheritanceMemberItem( + lineNumber: 11, + memberName: "override string Bar2.Poo { get; set; }", + targets: [new TargetInfo( + targetSymbolDisplayName: $"Bar.Poo", + locationTag: "target6", + relationship: InheritanceRelationship.OverriddenMember)]); + + var itemForPooInAbstractClass = new TestInheritanceMemberItem( + lineNumber: 5, + memberName: $"{modifier} int Bar.Poo {{ get; set; }}", + targets: [new TargetInfo( + targetSymbolDisplayName: "Bar2.Poo", + locationTag: "target5", + relationship: InheritanceRelationship.OverridingMember)]); + + var itemForFooInAbstractClass = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: $"{modifier} void Bar.Foo()", + targets: [new TargetInfo( + targetSymbolDisplayName: "Bar2.Foo", + locationTag: "target3", + relationship: InheritanceRelationship.OverridingMember)]); + + var itemForFooInClass = new TestInheritanceMemberItem( + lineNumber: 10, + memberName: "override void Bar2.Foo(int)", + targets: [new TargetInfo( + targetSymbolDisplayName: $"Bar.Foo", + locationTag: "target4", + relationship: InheritanceRelationship.OverriddenMember)]); + + var itemForBar = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "class Bar", + targets: [new TargetInfo( + targetSymbolDisplayName: "Bar2", + locationTag: "target1", + relationship: InheritanceRelationship.DerivedType)]); + + var itemForBar2 = new TestInheritanceMemberItem( + lineNumber: 8, + memberName: "class Bar2", + targets: [new TargetInfo( + targetSymbolDisplayName: "Bar", + locationTag: "target2", + relationship: InheritanceRelationship.BaseType)]); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.CSharp, + testHost, + itemForBar, + itemForBar2, + itemForFooInAbstractClass, + itemForFooInClass, + itemForPooInClass, + itemForPooInAbstractClass, + itemForEooInClass, + itemForEooInAbstractClass); + } + [Theory, CombinatorialData] public Task TestCSharpFindGenericsBaseType(TestHost testHost) { @@ -1029,36 +1148,36 @@ public class {|target1:Bar2|} : IBar, IBar var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar2", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForFooInIBar = new TestInheritanceMemberItem( lineNumber: 4, memberName: "void IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar2.Foo", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); // Only have one IBar item var itemForBar2 = new TestInheritanceMemberItem( lineNumber: 7, memberName: "class Bar2", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); // Only have one IBar.Foo item var itemForFooInBar2 = new TestInheritanceMemberItem( lineNumber: 9, memberName: "void Bar2.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Foo", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -1089,35 +1208,34 @@ abstract class {|target1:AbsBar|} : IBar var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "AbsBar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForFooInIBar = new TestInheritanceMemberItem( lineNumber: 4, memberName: "void IBar.Foo(T)", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "AbsBar.IBar.Foo", locationTag: "target4", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForAbsBar = new TestInheritanceMemberItem( lineNumber: 7, memberName: "class AbsBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForFooInAbsBar = new TestInheritanceMemberItem( lineNumber: 9, memberName: "void AbsBar.IBar.Foo(int)", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Foo", locationTag: "target3", - relationship: InheritanceRelationship.ImplementedMember) - )); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -1153,98 +1271,98 @@ public class {|target1:Class1|} : I1 var itemForI1 = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface I1", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Class1", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForM1InI1 = new TestInheritanceMemberItem( lineNumber: 4, memberName: "void I1.M1()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Class1.M1", locationTag: "target2", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForAbsClass1 = new TestInheritanceMemberItem( lineNumber: 11, memberName: "class Class1", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "I1", locationTag: "target5", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForM1InClass1 = new TestInheritanceMemberItem( lineNumber: 13, memberName: "static void Class1.M1()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "I1.M1", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForP1InI1 = new TestInheritanceMemberItem( lineNumber: 5, memberName: "int I1.P1 { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Class1.P1", locationTag: "target6", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForP1InClass1 = new TestInheritanceMemberItem( lineNumber: 14, memberName: "static int Class1.P1 { get; set; }", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "I1.P1", locationTag: "target7", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForE1InI1 = new TestInheritanceMemberItem( lineNumber: 6, memberName: "event EventHandler I1.e1", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Class1.e1", locationTag: "target8", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForE1InClass1 = new TestInheritanceMemberItem( lineNumber: 15, memberName: "static event EventHandler Class1.e1", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "I1.e1", locationTag: "target9", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForPlusOperatorInI1 = new TestInheritanceMemberItem( lineNumber: 7, memberName: "int I1.operator +(T)", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Class1.operator +", locationTag: "target10", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForPlusOperatorInClass1 = new TestInheritanceMemberItem( lineNumber: 16, memberName: "static int Class1.operator +(Class1)", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "I1.operator +", locationTag: "target11", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForIntOperatorInI1 = new TestInheritanceMemberItem( lineNumber: 8, memberName: "I1.implicit operator int(T)", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Class1.implicit operator int", locationTag: "target13", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForIntOperatorInClass1 = new TestInheritanceMemberItem( lineNumber: 17, memberName: "static Class1.implicit operator int(Class1)", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "I1.implicit operator int", locationTag: "target12", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -1284,26 +1402,26 @@ public partial class {|target3:Bar|} var itemOnLine2 = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar", relationship: InheritanceRelationship.ImplementingType, - "target2", "target3"))); + "target2", "target3")]); var itemOnLine6 = new TestInheritanceMemberItem( lineNumber: 6, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemOnLine10 = new TestInheritanceMemberItem( lineNumber: 10, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); return VerifyInSingleDocumentAsync( markup, @@ -1326,9 +1444,9 @@ public Task TestEmptyFileSingleGlobalImportInOtherFile(TestHost testHost) new TestInheritanceMemberItem( lineNumber: 0, memberName: string.Format(FeaturesResources.Directives_from_0, "Test2.cs"), - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "System", - relationship: InheritanceRelationship.InheritedImport, "target1")))); + relationship: InheritanceRelationship.InheritedImport, "target1")])); } [Theory, CombinatorialData] @@ -1345,13 +1463,15 @@ public Task TestEmptyFileMultipleGlobalImportInOtherFile(TestHost testHost) new TestInheritanceMemberItem( lineNumber: 0, memberName: string.Format(FeaturesResources.Directives_from_0, "Test2.cs"), - targets: ImmutableArray.Create( + targets: + [ new TargetInfo( - targetSymbolDisplayName: "System", - relationship: InheritanceRelationship.InheritedImport, "target1"), + targetSymbolDisplayName: "System", + relationship: InheritanceRelationship.InheritedImport, "target1"), new TargetInfo( targetSymbolDisplayName: "System.Collections", - relationship: InheritanceRelationship.InheritedImport, "target2")))); + relationship: InheritanceRelationship.InheritedImport, "target2"), + ])); } [Theory, CombinatorialData] @@ -1367,9 +1487,9 @@ public Task TestFileWithUsing_SingleGlobalImportInOtherFile(TestHost testHost) new TestInheritanceMemberItem( lineNumber: 1, memberName: string.Format(FeaturesResources.Directives_from_0, "Test2.cs"), - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "System", - relationship: InheritanceRelationship.InheritedImport, "target1")))); + relationship: InheritanceRelationship.InheritedImport, "target1")])); } [Theory, CombinatorialData] @@ -1386,9 +1506,9 @@ public Task TestIgnoreGlobalImportFromSameFile(TestHost testHost) new TestInheritanceMemberItem( lineNumber: 1, memberName: string.Format(FeaturesResources.Directives_from_0, "Test2.cs"), - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "System", - relationship: InheritanceRelationship.InheritedImport, "target1")))); + relationship: InheritanceRelationship.InheritedImport, "target1")])); } #endregion @@ -1423,18 +1543,18 @@ End Class var itemForBar = new TestInheritanceMemberItem( lineNumber: 3, memberName: "Class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IEnumerable", relationship: InheritanceRelationship.ImplementedInterface, - inMetadata: true))); + inMetadata: true)]); var itemForGetEnumerator = new TestInheritanceMemberItem( lineNumber: 5, memberName: "Function Bar.GetEnumerator() As IEnumerator", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IEnumerable.GetEnumerator", relationship: InheritanceRelationship.ImplementedMember, - inMetadata: true))); + inMetadata: true)]); return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, testHost, itemForBar, itemForGetEnumerator); } @@ -1451,18 +1571,18 @@ Implements IBar var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Interface IBar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 4, memberName: "Class Bar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); return VerifyInSingleDocumentAsync( markup, @@ -1485,18 +1605,18 @@ Inherits IBar2 var itemForIBar2 = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Interface IBar2", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForIBar = new TestInheritanceMemberItem( lineNumber: 4, memberName: "Interface IBar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar2", locationTag: "target2", - relationship: InheritanceRelationship.InheritedInterface))); + relationship: InheritanceRelationship.InheritedInterface)]); return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, testHost, itemForIBar2, itemForIBar); } @@ -1513,18 +1633,18 @@ Inherits Bar2 var itemForBar2 = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Class Bar2", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target1", - relationship: InheritanceRelationship.DerivedType))); + relationship: InheritanceRelationship.DerivedType)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 4, memberName: "Class Bar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar2", locationTag: "target2", - relationship: InheritanceRelationship.BaseType))); + relationship: InheritanceRelationship.BaseType)]); return VerifyInSingleDocumentAsync(markup, LanguageNames.VisualBasic, testHost, itemForBar2, itemForBar); } @@ -1561,18 +1681,19 @@ Implements IEnumerable new TestInheritanceMemberItem( lineNumber: 2, memberName: VBFeaturesResources.Project_level_Imports, - targets: ImmutableArray.Create( + targets: + [ new TargetInfo("System", InheritanceRelationship.InheritedImport), new TargetInfo("System.Collections.Generic", InheritanceRelationship.InheritedImport), - new TargetInfo("System.Linq", InheritanceRelationship.InheritedImport))), + new TargetInfo("System.Linq", InheritanceRelationship.InheritedImport), + ]), new TestInheritanceMemberItem( lineNumber: 3, memberName: "Class Bar", - targets: ImmutableArray.Create( - new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IEnumerable", relationship: InheritanceRelationship.ImplementedInterface, - inMetadata: true)))); + inMetadata: true)])); } [Theory, CombinatorialData] @@ -1590,34 +1711,34 @@ Implements IBar var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Interface IBar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 5, memberName: "Class Bar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForEventInInterface = new TestInheritanceMemberItem( lineNumber: 3, memberName: "Event IBar.e As EventHandler", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar.e", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForEventInClass = new TestInheritanceMemberItem( lineNumber: 7, memberName: "Event Bar.e As EventHandler", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar.e", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -1644,34 +1765,34 @@ End Event var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Interface IBar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 5, memberName: "Class Bar", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForEventInInterface = new TestInheritanceMemberItem( lineNumber: 3, memberName: "Event IBar.e As EventHandler", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "Bar.e", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForEventInClass = new TestInheritanceMemberItem( lineNumber: 7, memberName: "Event Bar.e As EventHandler", - ImmutableArray.Create(new TargetInfo( + [new TargetInfo( targetSymbolDisplayName: "IBar.e", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -1708,50 +1829,50 @@ End Function var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 7, memberName: "Class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForPooInInterface = new TestInheritanceMemberItem( lineNumber: 3, memberName: "Property IBar.Poo As Integer", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar.Poo", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForPooInClass = new TestInheritanceMemberItem( lineNumber: 9, memberName: "Property Bar.Poo As Integer", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Poo", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForFooInInterface = new TestInheritanceMemberItem( lineNumber: 4, memberName: "Function IBar.Foo() As Integer", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar.Foo", locationTag: "target5", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); var itemForFooInClass = new TestInheritanceMemberItem( lineNumber: 16, memberName: "Function Bar.Foo() As Integer", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Foo", locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -1781,34 +1902,34 @@ End Sub var itemForBar1 = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Class Bar1", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: $"Bar", locationTag: "target1", - relationship: InheritanceRelationship.DerivedType))); + relationship: InheritanceRelationship.DerivedType)]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 6, memberName: "Class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar1", locationTag: "target2", - relationship: InheritanceRelationship.BaseType))); + relationship: InheritanceRelationship.BaseType)]); var itemForFooInBar1 = new TestInheritanceMemberItem( lineNumber: 3, memberName: "MustOverride Sub Bar1.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar.Foo", locationTag: "target3", - relationship: InheritanceRelationship.OverridingMember))); + relationship: InheritanceRelationship.OverridingMember)]); var itemForFooInBar = new TestInheritanceMemberItem( lineNumber: 8, memberName: "Overrides Sub Bar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar1.Foo", locationTag: "target4", - relationship: InheritanceRelationship.OverriddenMember))); + relationship: InheritanceRelationship.OverriddenMember)]); return VerifyInSingleDocumentAsync( markup, @@ -1859,77 +1980,93 @@ End Sub var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Interface IBar", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Bar1", - locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType), + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "Bar1", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType), new TargetInfo( targetSymbolDisplayName: "Bar2", locationTag: "target5", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType), + ]); var itemForFooInIBar = new TestInheritanceMemberItem( lineNumber: 3, memberName: "Sub IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Bar1.Foo", - locationTag: "target2", - relationship: InheritanceRelationship.ImplementingMember), + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "Bar1.Foo", + locationTag: "target2", + relationship: InheritanceRelationship.ImplementingMember), new TargetInfo( targetSymbolDisplayName: "Bar2.Foo", locationTag: "target3", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember), + ]); var itemForBar1 = new TestInheritanceMemberItem( lineNumber: 6, memberName: "Class Bar1", - targets: ImmutableArray.Create( + targets: + [ new TargetInfo( - targetSymbolDisplayName: "Bar2", - locationTag: "target5", - relationship: InheritanceRelationship.DerivedType), + targetSymbolDisplayName: "Bar2", + locationTag: "target5", + relationship: InheritanceRelationship.DerivedType), new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target4", relationship: InheritanceRelationship.ImplementedInterface) - )); +, + ]); var itemForFooInBar1 = new TestInheritanceMemberItem( lineNumber: 8, memberName: "Overridable Sub Bar1.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "IBar.Foo", - locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember), + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "IBar.Foo", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember), new TargetInfo( targetSymbolDisplayName: "Bar2.Foo", locationTag: "target3", - relationship: InheritanceRelationship.OverridingMember))); + relationship: InheritanceRelationship.OverridingMember), + ]); var itemForBar2 = new TestInheritanceMemberItem( lineNumber: 12, memberName: "Class Bar2", - targets: ImmutableArray.Create( + targets: + [ new TargetInfo( - targetSymbolDisplayName: "Bar1", - locationTag: "target1", - relationship: InheritanceRelationship.BaseType), + targetSymbolDisplayName: "Bar1", + locationTag: "target1", + relationship: InheritanceRelationship.BaseType), new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target4", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface), + ]); var itemForFooInBar2 = new TestInheritanceMemberItem( lineNumber: 14, memberName: "Overrides Sub Bar2.Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "IBar.Foo", - locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember), + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "IBar.Foo", + locationTag: "target6", + relationship: InheritanceRelationship.ImplementedMember), new TargetInfo( targetSymbolDisplayName: "Bar1.Foo", locationTag: "target2", - relationship: InheritanceRelationship.OverriddenMember))); + relationship: InheritanceRelationship.OverriddenMember), + ]); return VerifyInSingleDocumentAsync( testDuplicate ? markup2 : markup1, @@ -1967,46 +2104,49 @@ End Sub var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "Interface IBar(Of T)", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForFooInIBar = new TestInheritanceMemberItem( lineNumber: 3, memberName: "Sub IBar(Of T).Foo()", - targets: ImmutableArray.Create(new TargetInfo( - targetSymbolDisplayName: "Bar.Foo", - locationTag: "target3", + targets: + [ + new TargetInfo( + targetSymbolDisplayName: "Bar.Foo", + locationTag: "target3", + relationship: InheritanceRelationship.ImplementingMember), + new TargetInfo( + targetSymbolDisplayName: "Bar.IBar_Foo", + locationTag: "target4", relationship: InheritanceRelationship.ImplementingMember), - new TargetInfo( - targetSymbolDisplayName: "Bar.IBar_Foo", - locationTag: "target4", - relationship: InheritanceRelationship.ImplementingMember))); + ]); var itemForBar = new TestInheritanceMemberItem( lineNumber: 6, memberName: "Class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar(Of T)", locationTag: "target5", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForFooInBar = new TestInheritanceMemberItem( lineNumber: 10, memberName: "Sub Bar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar(Of T).Foo", locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForIBar_FooInBar = new TestInheritanceMemberItem( lineNumber: 14, memberName: "Sub Bar.IBar_Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar(Of T).Foo", locationTag: "target6", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); return VerifyInSingleDocumentAsync( markup, @@ -2044,34 +2184,34 @@ End Interface var itemForBar = new TestInheritanceMemberItem( lineNumber: 5, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForFooInMarkup1 = new TestInheritanceMemberItem( lineNumber: 7, memberName: "void Bar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Foo", locationTag: "target3", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForIBar = new TestInheritanceMemberItem( lineNumber: 3, memberName: "Interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForFooInMarkup2 = new TestInheritanceMemberItem( lineNumber: 4, memberName: "Sub IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar.Foo", locationTag: "target4", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); return VerifyInDifferentProjectsAsync( (markup1, LanguageNames.CSharp), @@ -2107,42 +2247,44 @@ public interface {|target1:IBar|} new TestInheritanceMemberItem( lineNumber: 2, memberName: VBFeaturesResources.Project_level_Imports, - targets: ImmutableArray.Create( + targets: + [ new TargetInfo("System", InheritanceRelationship.InheritedImport), new TargetInfo("System.Collections.Generic", InheritanceRelationship.InheritedImport), - new TargetInfo("System.Linq", InheritanceRelationship.InheritedImport))); + new TargetInfo("System.Linq", InheritanceRelationship.InheritedImport), + ]); var itemForBar44 = new TestInheritanceMemberItem( lineNumber: 4, memberName: "Class Bar44", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target1", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForFooInMarkup1 = new TestInheritanceMemberItem( lineNumber: 7, memberName: "Sub Bar44.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar.Foo", locationTag: "target3", - relationship: InheritanceRelationship.ImplementedMember))); + relationship: InheritanceRelationship.ImplementedMember)]); var itemForIBar = new TestInheritanceMemberItem( lineNumber: 4, memberName: "interface IBar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar44", locationTag: "target2", - relationship: InheritanceRelationship.ImplementingType))); + relationship: InheritanceRelationship.ImplementingType)]); var itemForFooInMarkup2 = new TestInheritanceMemberItem( lineNumber: 6, memberName: "void IBar.Foo()", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "Bar44.Foo", locationTag: "target4", - relationship: InheritanceRelationship.ImplementingMember))); + relationship: InheritanceRelationship.ImplementingMember)]); return VerifyInDifferentProjectsAsync( (markup1, LanguageNames.VisualBasic), @@ -2177,35 +2319,37 @@ End Class var itemForBarInMarkup1 = new TestInheritanceMemberItem( lineNumber: 5, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForIBar = new TestInheritanceMemberItem( lineNumber: 3, memberName: "Interface IBar", - targets: ImmutableArray.Create( + targets: + [ new TargetInfo( - targetSymbolDisplayName: "Bar", - locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType, - languageGlyph: Glyph.CSharpFile, - projectName: "Assembly1"), + targetSymbolDisplayName: "Bar", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType, + languageGlyph: Glyph.CSharpFile, + projectName: "Assembly1"), new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target3", relationship: InheritanceRelationship.ImplementingType, languageGlyph: Glyph.BasicFile, - projectName: "Assembly2"))); + projectName: "Assembly2"), + ]); var itemForBarInMarkup2 = new TestInheritanceMemberItem( lineNumber: 6, memberName: "Class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); return VerifyInDifferentProjectsAsync( (markup1, LanguageNames.CSharp), @@ -2240,35 +2384,37 @@ public class {|target3:Bar|} var itemForBarInMarkup1 = new TestInheritanceMemberItem( lineNumber: 5, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); var itemForIBar = new TestInheritanceMemberItem( lineNumber: 3, memberName: "interface IBar", - targets: ImmutableArray.Create( + targets: + [ new TargetInfo( - targetSymbolDisplayName: "Bar", - locationTag: "target1", - relationship: InheritanceRelationship.ImplementingType, - languageGlyph: Glyph.CSharpFile, - projectName: "Assembly1"), + targetSymbolDisplayName: "Bar", + locationTag: "target1", + relationship: InheritanceRelationship.ImplementingType, + languageGlyph: Glyph.CSharpFile, + projectName: "Assembly1"), new TargetInfo( targetSymbolDisplayName: "Bar", locationTag: "target3", relationship: InheritanceRelationship.ImplementingType, languageGlyph: Glyph.CSharpFile, - projectName: "Assembly2"))); + projectName: "Assembly2"), + ]); var itemForBarInMarkup2 = new TestInheritanceMemberItem( lineNumber: 6, memberName: "class Bar", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "IBar", locationTag: "target2", - relationship: InheritanceRelationship.ImplementedInterface))); + relationship: InheritanceRelationship.ImplementedInterface)]); return VerifyInDifferentProjectsAsync( (markup1, LanguageNames.CSharp), @@ -2342,9 +2488,9 @@ await VerifyInSingleDocumentAsync(correctSearchingCode, memberItems: [new TestInheritanceMemberItem( lineNumber: 2, memberName: "class B", - targets: ImmutableArray.Create(new TargetInfo( + targets: [new TargetInfo( targetSymbolDisplayName: "C", locationTag: "target", - relationship: InheritanceRelationship.BaseType)))]); + relationship: InheritanceRelationship.BaseType)])]); } } diff --git a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.CSharp.cs b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.CSharp.cs index 29e81ac37afbf..dc5c07138302f 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.CSharp.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.CSharp.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Threading.Tasks; using Microsoft.CodeAnalysis.MetadataAsSource; using Microsoft.CodeAnalysis.Test.Utilities; @@ -20,13 +18,17 @@ public class CSharp : AbstractMetadataAsSourceTests [Fact] public void ExtractXMLFromDocComment() { - var docCommentText = @"/// -/// I am the very model of a modern major general. -/// "; + var docCommentText = """ + /// + /// I am the very model of a modern major general. + /// + """; - var expectedXMLFragment = @" - I am the very model of a modern major general. - "; + var expectedXMLFragment = """ + + I am the very model of a modern major general. + + """; var extractedXMLFragment = DocumentationCommentUtilities.ExtractXMLFragment(docCommentText, "///"); @@ -42,35 +44,39 @@ public async Task TestNativeInteger(bool signaturesOnly) var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|C|] -{{ - public nint i; - public nuint i2; - - public C(); -}}", - false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ - public nint i; - - public nuint i2; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|C|] + { + public nint i; + public nuint i2; + + public C(); + } + """, + false => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + public nint i; + + public nuint i2; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, languageVersion: "Preview", metadataLanguageVersion: "Preview", expected: expected, signaturesOnly: signaturesOnly); @@ -79,42 +85,48 @@ public class [|C|] [Theory, CombinatorialData] public async Task TestInitOnlyProperty(bool signaturesOnly) { - var metadataSource = @"public class C { public int Property { get; init; } } -namespace System.Runtime.CompilerServices -{ - public sealed class IsExternalInit { } -} -"; + var metadataSource = """ + public class C { public int Property { get; init; } } + namespace System.Runtime.CompilerServices + { + public sealed class IsExternalInit { } + } + + """; var symbolName = "C"; var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|C|] -{{ - public C(); - - public int Property {{ get; init; }} -}}", - false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ - public int Property {{ get; init; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|C|] + { + public C(); + + public int Property { get; init; } + } + """, + false => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + public int Property { get; init; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, languageVersion: "Preview", metadataLanguageVersion: "Preview", expected: expected, signaturesOnly: signaturesOnly); @@ -128,39 +140,43 @@ public async Task TestTupleWithNames(bool signaturesOnly) var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System.Runtime.CompilerServices; - -public class [|C|] -{{ - [TupleElementNames(new[] {{ ""a"", ""b"" }})] - public (int a, int b) t; - - public C(); -}}", - false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ - public (int a, int b) t; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} ------------------- -{string.Format(FeaturesResources.Resolve_0, "System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51")} -{string.Format(FeaturesResources.Found_single_assembly_0, "System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51")} -{string.Format(FeaturesResources.Load_from_0, "System.ValueTuple (net461)")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System.Runtime.CompilerServices; + + public class [|C|] + { + [TupleElementNames(new[] { "a", "b" })] + public (int a, int b) t; + + public C(); + } + """, + false => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + public (int a, int b) t; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51")}} + {{string.Format(FeaturesResources.Load_from_0, "System.ValueTuple (net461)")}} + #endif + """, }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, expected: expected, signaturesOnly: signaturesOnly); @@ -173,214 +189,218 @@ public async Task TestValueTuple(bool signaturesOnly) var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 -// System.ValueTuple (net461) -#endregion - -using System.Collections; - -namespace System -{{ - public struct [|ValueTuple|] : IEquatable, IStructuralEquatable, IStructuralComparable, IComparable, IComparable, ITupleInternal - {{ - public static ValueTuple Create(); - public static ValueTuple Create(T1 item1); - public static (T1, T2) Create(T1 item1, T2 item2); - public static (T1, T2, T3) Create(T1 item1, T2 item2, T3 item3); - public static (T1, T2, T3, T4) Create(T1 item1, T2 item2, T3 item3, T4 item4); - public static (T1, T2, T3, T4, T5) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5); - public static (T1, T2, T3, T4, T5, T6) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6); - public static (T1, T2, T3, T4, T5, T6, T7) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7); - public static (T1, T2, T3, T4, T5, T6, T7, T8) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8); - public int CompareTo(ValueTuple other); - public override bool Equals(object obj); - public bool Equals(ValueTuple other); - public override int GetHashCode(); - public override string ToString(); - }} -}}", - false => $@"#region {FeaturesResources.Assembly} System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 -// System.ValueTuple (net461) -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Collections; -using System.Numerics.Hashing; -using System.Runtime.InteropServices; - -namespace System; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct [|ValueTuple|] : IEquatable, IStructuralEquatable, IStructuralComparable, IComparable, IComparable, ITupleInternal -{{ - int ITupleInternal.Size => 0; - - public override bool Equals(object obj) - {{ - return obj is ValueTuple; - }} - - public bool Equals(ValueTuple other) - {{ - return true; - }} - - bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) - {{ - return other is ValueTuple; - }} - - int IComparable.CompareTo(object other) - {{ - if (other == null) - {{ - return 1; - }} - - if (!(other is ValueTuple)) - {{ - throw new ArgumentException(System.SR.ArgumentException_ValueTupleIncorrectType, ""other""); - }} - - return 0; - }} - - public int CompareTo(ValueTuple other) - {{ - return 0; - }} - - int IStructuralComparable.CompareTo(object other, IComparer comparer) - {{ - if (other == null) - {{ - return 1; - }} - - if (!(other is ValueTuple)) - {{ - throw new ArgumentException(System.SR.ArgumentException_ValueTupleIncorrectType, ""other""); - }} - - return 0; - }} - - public override int GetHashCode() - {{ - return 0; - }} - - int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) - {{ - return 0; - }} - - int ITupleInternal.GetHashCode(IEqualityComparer comparer) - {{ - return 0; - }} - - public override string ToString() - {{ - return ""()""; - }} - - string ITupleInternal.ToStringEnd() - {{ - return "")""; - }} - - public static ValueTuple Create() - {{ - return default(ValueTuple); - }} - - public static ValueTuple Create(T1 item1) - {{ - return new ValueTuple(item1); - }} - - public static (T1, T2) Create(T1 item1, T2 item2) - {{ - return (item1, item2); - }} - - public static (T1, T2, T3) Create(T1 item1, T2 item2, T3 item3) - {{ - return (item1, item2, item3); - }} - - public static (T1, T2, T3, T4) Create(T1 item1, T2 item2, T3 item3, T4 item4) - {{ - return (item1, item2, item3, item4); - }} - - public static (T1, T2, T3, T4, T5) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) - {{ - return (item1, item2, item3, item4, item5); - }} - - public static (T1, T2, T3, T4, T5, T6) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) - {{ - return (item1, item2, item3, item4, item5, item6); - }} - - public static (T1, T2, T3, T4, T5, T6, T7) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) - {{ - return (item1, item2, item3, item4, item5, item6, item7); - }} - - public static (T1, T2, T3, T4, T5, T6, T7, T8) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) - {{ - return new ValueTuple>(item1, item2, item3, item4, item5, item6, item7, Create(item8)); - }} - - internal static int CombineHashCodes(int h1, int h2) - {{ - return HashHelpers.Combine(HashHelpers.Combine(HashHelpers.RandomSeed, h1), h2); - }} - - internal static int CombineHashCodes(int h1, int h2, int h3) - {{ - return HashHelpers.Combine(CombineHashCodes(h1, h2), h3); - }} - - internal static int CombineHashCodes(int h1, int h2, int h3, int h4) - {{ - return HashHelpers.Combine(CombineHashCodes(h1, h2, h3), h4); - }} - - internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5) - {{ - return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4), h5); - }} - - internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6) - {{ - return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5), h6); - }} - - internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7) - {{ - return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6), h7); - }} - - internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8) - {{ - return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7), h8); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} ------------------- -{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 + // System.ValueTuple (net461) + #endregion + + using System.Collections; + + namespace System + { + public struct [|ValueTuple|] : IEquatable, IStructuralEquatable, IStructuralComparable, IComparable, IComparable, ITupleInternal + { + public static ValueTuple Create(); + public static ValueTuple Create(T1 item1); + public static (T1, T2) Create(T1 item1, T2 item2); + public static (T1, T2, T3) Create(T1 item1, T2 item2, T3 item3); + public static (T1, T2, T3, T4) Create(T1 item1, T2 item2, T3 item3, T4 item4); + public static (T1, T2, T3, T4, T5) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5); + public static (T1, T2, T3, T4, T5, T6) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6); + public static (T1, T2, T3, T4, T5, T6, T7) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7); + public static (T1, T2, T3, T4, T5, T6, T7, T8) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8); + public int CompareTo(ValueTuple other); + public override bool Equals(object obj); + public bool Equals(ValueTuple other); + public override int GetHashCode(); + public override string ToString(); + } + } + """, + false => $$""" + #region {{FeaturesResources.Assembly}} System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 + // System.ValueTuple (net461) + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Collections; + using System.Numerics.Hashing; + using System.Runtime.InteropServices; + + namespace System; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct [|ValueTuple|] : IEquatable, IStructuralEquatable, IStructuralComparable, IComparable, IComparable, ITupleInternal + { + int ITupleInternal.Size => 0; + + public override bool Equals(object obj) + { + return obj is ValueTuple; + } + + public bool Equals(ValueTuple other) + { + return true; + } + + bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) + { + return other is ValueTuple; + } + + int IComparable.CompareTo(object other) + { + if (other == null) + { + return 1; + } + + if (!(other is ValueTuple)) + { + throw new ArgumentException(System.SR.ArgumentException_ValueTupleIncorrectType, "other"); + } + + return 0; + } + + public int CompareTo(ValueTuple other) + { + return 0; + } + + int IStructuralComparable.CompareTo(object other, IComparer comparer) + { + if (other == null) + { + return 1; + } + + if (!(other is ValueTuple)) + { + throw new ArgumentException(System.SR.ArgumentException_ValueTupleIncorrectType, "other"); + } + + return 0; + } + + public override int GetHashCode() + { + return 0; + } + + int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) + { + return 0; + } + + int ITupleInternal.GetHashCode(IEqualityComparer comparer) + { + return 0; + } + + public override string ToString() + { + return "()"; + } + + string ITupleInternal.ToStringEnd() + { + return ")"; + } + + public static ValueTuple Create() + { + return default(ValueTuple); + } + + public static ValueTuple Create(T1 item1) + { + return new ValueTuple(item1); + } + + public static (T1, T2) Create(T1 item1, T2 item2) + { + return (item1, item2); + } + + public static (T1, T2, T3) Create(T1 item1, T2 item2, T3 item3) + { + return (item1, item2, item3); + } + + public static (T1, T2, T3, T4) Create(T1 item1, T2 item2, T3 item3, T4 item4) + { + return (item1, item2, item3, item4); + } + + public static (T1, T2, T3, T4, T5) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) + { + return (item1, item2, item3, item4, item5); + } + + public static (T1, T2, T3, T4, T5, T6) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) + { + return (item1, item2, item3, item4, item5, item6); + } + + public static (T1, T2, T3, T4, T5, T6, T7) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) + { + return (item1, item2, item3, item4, item5, item6, item7); + } + + public static (T1, T2, T3, T4, T5, T6, T7, T8) Create(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) + { + return new ValueTuple>(item1, item2, item3, item4, item5, item6, item7, Create(item8)); + } + + internal static int CombineHashCodes(int h1, int h2) + { + return HashHelpers.Combine(HashHelpers.Combine(HashHelpers.RandomSeed, h1), h2); + } + + internal static int CombineHashCodes(int h1, int h2, int h3) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2), h3); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3), h4); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4), h5); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5), h6); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6), h7); + } + + internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8) + { + return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7), h8); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")}} + #endif + """, }; await context.GenerateAndVerifySourceAsync("System.ValueTuple", expected, signaturesOnly: signaturesOnly); @@ -394,34 +414,38 @@ public async Task TestExtendedPartialMethod1(bool signaturesOnly) var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|C|] -{{ - public C(); - - public void F(); -}}", - false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ - public void F() - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|C|] + { + public C(); + + public void F(); + } + """, + false => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + public void F() + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, languageVersion: "Preview", metadataLanguageVersion: "Preview", expected: expected, signaturesOnly: signaturesOnly); @@ -436,56 +460,60 @@ public async Task TestRecordType(bool signaturesOnly) var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -#nullable enable - -using System; -using System.Runtime.CompilerServices; -using System.Text; - -public record [|R|] : IEquatable -{{ - public R(); - [CompilerGenerated] - protected R(R original); - - [CompilerGenerated] - protected virtual Type EqualityContract {{ get; }} - - [CompilerGenerated] - public virtual R $(); - [CompilerGenerated] - public override bool Equals(object? obj); - [CompilerGenerated] - public virtual bool Equals(R? other); - [CompilerGenerated] - public override int GetHashCode(); - [CompilerGenerated] - public override string ToString(); - [CompilerGenerated] - protected virtual bool PrintMembers(StringBuilder builder); - - [CompilerGenerated] - public static bool operator ==(R? left, R? right); - [CompilerGenerated] - public static bool operator !=(R? left, R? right); -}}", - false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public record [|R|]; -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + #nullable enable + + using System; + using System.Runtime.CompilerServices; + using System.Text; + + public record [|R|] : IEquatable + { + public R(); + [CompilerGenerated] + protected R(R original); + + [CompilerGenerated] + protected virtual Type EqualityContract { get; } + + [CompilerGenerated] + public virtual R $(); + [CompilerGenerated] + public override bool Equals(object? obj); + [CompilerGenerated] + public virtual bool Equals(R? other); + [CompilerGenerated] + public override int GetHashCode(); + [CompilerGenerated] + public override string ToString(); + [CompilerGenerated] + protected virtual bool PrintMembers(StringBuilder builder); + + [CompilerGenerated] + public static bool operator ==(R? left, R? right); + [CompilerGenerated] + public static bool operator !=(R? left, R? right); + } + """, + false => $""" + #region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {CodeAnalysisResources.InMemoryAssembly} + // Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} + #endregion + + public record [|R|]; + #if false // {FeaturesResources.Decompilation_log} + {string.Format(FeaturesResources._0_items_in_cache, 6)} + ------------------ + {string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} + {string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} + {string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} + #endif + """, }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, expected: expected, signaturesOnly: signaturesOnly); @@ -498,87 +526,93 @@ public record [|R|]; [WorkItem("https://github.com/dotnet/roslyn/issues/42986")] public async Task TestCheckedOperators(bool signaturesOnly) { - var metadataSource = @" -public class C -{ - public static explicit operator string(C x) => throw new System.Exception(); + var metadataSource = """ - public static explicit operator checked string(C x) => throw new System.Exception(); + public class C + { + public static explicit operator string(C x) => throw new System.Exception(); + + public static explicit operator checked string(C x) => throw new System.Exception(); - public static C operator -(C x) => throw new System.Exception(); + public static C operator -(C x) => throw new System.Exception(); - public static C operator checked -(C x) => throw new System.Exception(); + public static C operator checked -(C x) => throw new System.Exception(); - public static C operator +(C x, C y) => throw new System.Exception(); + public static C operator +(C x, C y) => throw new System.Exception(); - public static C operator checked +(C x, C y) => throw new System.Exception(); -}"; + public static C operator checked +(C x, C y) => throw new System.Exception(); + } + """; var symbolName = "C"; var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|C|] -{{ - public C(); - - public static C operator +(C x, C y); - public static C operator checked +(C x, C y); - public static C operator -(C x); - public static C operator checked -(C x); - - public static explicit operator string(C x); - public static explicit operator checked string(C x); -}}", - false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; - -public class [|C|] -{{ - public static explicit operator string(C x) - {{ - throw new Exception(); - }} - - public static explicit operator checked string(C x) - {{ - throw new Exception(); - }} - - public static C operator -(C x) - {{ - throw new Exception(); - }} - - public static C operator checked -(C x) - {{ - throw new Exception(); - }} - - public static C operator +(C x, C y) - {{ - throw new Exception(); - }} - - public static C operator checked +(C x, C y) - {{ - throw new Exception(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|C|] + { + public C(); + + public static C operator +(C x, C y); + public static C operator checked +(C x, C y); + public static C operator -(C x); + public static C operator checked -(C x); + + public static explicit operator string(C x); + public static explicit operator checked string(C x); + } + """, + false => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + public class [|C|] + { + public static explicit operator string(C x) + { + throw new Exception(); + } + + public static explicit operator checked string(C x) + { + throw new Exception(); + } + + public static C operator -(C x) + { + throw new Exception(); + } + + public static C operator checked -(C x) + { + throw new Exception(); + } + + public static C operator +(C x, C y) + { + throw new Exception(); + } + + public static C operator checked +(C x, C y) + { + throw new Exception(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, languageVersion: "Preview", metadataLanguageVersion: "Preview", expected: expected, signaturesOnly: signaturesOnly); @@ -587,43 +621,47 @@ public static explicit operator checked string(C x) [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/60567")] public async Task TestStaticInterfaceMembers() { - var metadataSource = @" -interface I where T : I -{ - static abstract T P { get; set; } - static abstract event System.Action E; - static abstract void M(); - static void NonAbstract() { } - static abstract T operator +(T l, T r); - static abstract bool operator ==(T l, T r); - static abstract bool operator !=(T l, T r); - static abstract implicit operator T(string s); - static abstract explicit operator string(T t); -}"; + var metadataSource = """ + + interface I where T : I + { + static abstract T P { get; set; } + static abstract event System.Action E; + static abstract void M(); + static void NonAbstract() { } + static abstract T operator +(T l, T r); + static abstract bool operator ==(T l, T r); + static abstract bool operator !=(T l, T r); + static abstract implicit operator T(string s); + static abstract explicit operator string(T t); + } + """; var symbolName = "I`1.M"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion -using System; + using System; -internal interface I where T : I -{{ - static abstract T P {{ get; set; }} + internal interface I where T : I + { + static abstract T P { get; set; } - static abstract event Action E; + static abstract event Action E; - static abstract void [|M|](); - static void NonAbstract(); + static abstract void [|M|](); + static void NonAbstract(); - static abstract T operator +(T l, T r); - static abstract bool operator ==(T l, T r); - static abstract bool operator !=(T l, T r); + static abstract T operator +(T l, T r); + static abstract bool operator ==(T l, T r); + static abstract bool operator !=(T l, T r); - static abstract implicit operator T(string s); - static abstract explicit operator string(T t); -}}"; + static abstract implicit operator T(string s); + static abstract explicit operator string(T t); + } + """; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, languageVersion: "Preview", metadataLanguageVersion: "Preview", expected: expected, signaturesOnly: true, metadataCommonReferences: "CommonReferencesNet6"); } @@ -636,35 +674,39 @@ public async Task UnsignedRightShift(bool signaturesOnly) var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public C(); - - public static C operator [|>>>|](C x, int y); -}}", - false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public static C operator [|>>>|](C x, int y) - {{ - return x; - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public C(); + + public static C operator [|>>>|](C x, int y); + } + """, + false => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public static C operator [|>>>|](C x, int y) + { + return x; + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, expected: expected, signaturesOnly: signaturesOnly, languageVersion: "Preview", metadataLanguageVersion: "Preview"); @@ -699,36 +741,40 @@ public CompilerFeatureRequiredAttribute(string featureName) // ICSharpDecompiler does not yet support decoding required members nicely var expected = signaturesOnly switch { - true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|C|] -{{ - public required int Field; - - public C(); - - public required int Property {{ get; set; }} -}}", - false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ - public required int Field; - - public required int Property {{ get; set; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + true => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|C|] + { + public required int Field; + + public C(); + + public required int Property { get; set; } + } + """, + false => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + public required int Field; + + public required int Property { get; set; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, }; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, languageVersion: "Preview", metadataLanguageVersion: "Preview", expected: expected, signaturesOnly: signaturesOnly); diff --git a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs index f74a6ef7371d3..e0c0bd654e729 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs @@ -2,20 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeGeneration; +using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; using CS = Microsoft.CodeAnalysis.CSharp; using VB = Microsoft.CodeAnalysis.VisualBasic; -using Microsoft.CodeAnalysis.CSharp.Formatting; -using System.Threading; namespace Microsoft.CodeAnalysis.Editor.UnitTests.MetadataAsSource; @@ -45,51 +44,59 @@ public async Task TestClass(OriginatingProjectLanguage language, bool signatures var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|C|] -{{ - public C(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class [|C|] - Public Sub New() -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|C|] + { + public C(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class [|C|] + Public Sub New() + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -104,49 +111,57 @@ public async Task TestInterface(OriginatingProjectLanguage language, bool signat var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public interface [|I|] -{{ -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Interface [|I|] -End Interface", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public interface [|I|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public interface [|I|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public interface [|I|] + { + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Interface [|I|] + End Interface + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public interface [|I|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public interface [|I|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -161,51 +176,59 @@ public async Task TestConstructor(OriginatingProjectLanguage language, bool sign var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public [|C|](); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class C - Public Sub [|New|]() -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public [|C|](); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class C + Public Sub [|New|]() + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -220,61 +243,69 @@ public async Task TestMethod(OriginatingProjectLanguage language, bool signature var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public C(); - - public void [|Goo|](); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class C - Public Sub New() - - Public Sub [|Goo|]() -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public void [|Goo|]() - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public void [|Goo|]() - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public C(); + + public void [|Goo|](); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class C + Public Sub New() + + Public Sub [|Goo|]() + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public void [|Goo|]() + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public void [|Goo|]() + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -289,57 +320,65 @@ public async Task TestField(OriginatingProjectLanguage language, bool signatures var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public string [|S|]; - - public C(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class C - Public [|S|] As String - - Public Sub New() -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public string [|S|]; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public string [|S|]; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public string [|S|]; + + public C(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class C + Public [|S|] As String + + Public Sub New() + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public string [|S|]; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public string [|S|]; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -354,57 +393,65 @@ public async Task TestProperty(OriginatingProjectLanguage language, bool signatu var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public C(); - - public string [|S|] {{ get; protected set; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class C - Public Sub New() - - Public Property [|S|] As String -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public string [|S|] {{ get; protected set; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public string [|S|] {{ get; protected set; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public C(); + + public string [|S|] { get; protected set; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class C + Public Sub New() + + Public Property [|S|] As String + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public string [|S|] { get; protected set; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public string [|S|] { get; protected set; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -420,65 +467,73 @@ public async Task TestEvent(OriginatingProjectLanguage language, bool signatures var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System; - -public class C -{{ - public C(); - - public event Action [|E|]; -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System - -Public Class C - Public Sub New() - - Public Event [|E|] As Action -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; - -public class C -{{ - public event Action [|E|]; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; - -public class C -{{ - public event Action [|E|]; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System; + + public class C + { + public C(); + + public event Action [|E|]; + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + + Public Class C + Public Sub New() + + Public Event [|E|] As Action + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + public class C + { + public event Action [|E|]; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + public class C + { + public event Action [|E|]; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -493,66 +548,74 @@ public async Task TestNestedType(OriginatingProjectLanguage language, bool signa var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public C(); - - protected class [|D|] - {{ - public D(); - }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class C - Public Sub New() - - Protected Class [|D|] - Public Sub New() - End Class -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - protected class [|D|] - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - protected class [|D|] - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public C(); + + protected class [|D|] + { + public D(); + } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class C + Public Sub New() + + Protected Class [|D|] + Public Sub New() + End Class + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + protected class [|D|] + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + protected class [|D|] + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -567,61 +630,69 @@ public async Task TestEnum(OriginatingProjectLanguage language, bool signaturesO var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public enum [|E|] -{{ - A = 0, - B = 1, - C = 2 -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Enum [|E|] - A = 0 - B = 1 - C = 2 -End Enum", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum [|E|] -{{ - A, - B, - C -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum [|E|] -{{ - A, - B, - C -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public enum [|E|] + { + A = 0, + B = 1, + C = 2 + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Enum [|E|] + A = 0 + B = 1 + C = 2 + End Enum + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum [|E|] + { + A, + B, + C + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum [|E|] + { + A, + B, + C + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -636,61 +707,69 @@ public async Task TestEnumFromField(OriginatingProjectLanguage language, bool si var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public enum E -{{ - A = 0, - B = 1, - [|C|] = 2 -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Enum E - A = 0 - B = 1 - [|C|] = 2 -End Enum", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum E -{{ - A, - B, - [|C|] -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum E -{{ - A, - B, - [|C|] -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public enum E + { + A = 0, + B = 1, + [|C|] = 2 + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Enum E + A = 0 + B = 1 + [|C|] = 2 + End Enum + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum E + { + A, + B, + [|C|] + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum E + { + A, + B, + [|C|] + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -705,61 +784,69 @@ public async Task TestEnumWithUnderlyingType(OriginatingProjectLanguage language var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public enum E : short -{{ - A = 0, - B = 1, - [|C|] = 2 -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Enum E As Short - A = 0 - B = 1 - [|C|] = 2 -End Enum", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum E : short -{{ - A, - B, - [|C|] -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum E : short -{{ - A, - B, - [|C|] -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public enum E : short + { + A = 0, + B = 1, + [|C|] = 2 + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Enum E As Short + A = 0 + B = 1 + [|C|] = 2 + End Enum + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum E : short + { + A, + B, + [|C|] + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum E : short + { + A, + B, + [|C|] + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -774,53 +861,61 @@ public async Task TestEnumWithOverflowingUnderlyingType(OriginatingProjectLangua var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public enum E : ulong -{{ - [|A|] = 9223372036854775808 -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Enum E As ULong - [|A|] = 9223372036854775808UL -End Enum", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum E : ulong -{{ - [|A|] = 9223372036854775808uL -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum E : ulong -{{ - [|A|] = 9223372036854775808uL -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public enum E : ulong + { + [|A|] = 9223372036854775808 + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Enum E As ULong + [|A|] = 9223372036854775808UL + End Enum + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum E : ulong + { + [|A|] = 9223372036854775808uL + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum E : ulong + { + [|A|] = 9223372036854775808uL + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -835,61 +930,69 @@ public async Task TestEnumWithDifferentValues(OriginatingProjectLanguage languag var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public enum E : short -{{ - A = 1, - B = 2, - [|C|] = 3 -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Enum E As Short - A = 1 - B = 2 - [|C|] = 3 -End Enum", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum E : short -{{ - A = 1, - B, - [|C|] -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public enum E : short -{{ - A = 1, - B, - [|C|] -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public enum E : short + { + A = 1, + B = 2, + [|C|] = 3 + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Enum E As Short + A = 1 + B = 2 + [|C|] = 3 + End Enum + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum E : short + { + A = 1, + B, + [|C|] + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public enum E : short + { + A = 1, + B, + [|C|] + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -904,60 +1007,68 @@ public async Task TestTypeInNamespace(OriginatingProjectLanguage language, bool var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -namespace N -{{ - public class [|C|] - {{ - public C(); - }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Namespace N - Public Class [|C|] - Public Sub New() - End Class -End Namespace", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -namespace N; - -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -namespace N; - -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + namespace N + { + public class [|C|] + { + public C(); + } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Namespace N + Public Class [|C|] + Public Sub New() + End Class + End Namespace + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + namespace N; + + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + namespace N; + + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -973,16 +1084,18 @@ public async Task TestTypeInFileScopedNamespace1() LanguageNames.CSharp, [metadataSource], languageVersion: "10", fileScopedNamespaces: true); await context.GenerateAndVerifySourceAsync("N.C", - $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion -namespace N; + namespace N; -public class [|C|] -{{ - public C(); -}}"); + public class [|C|] + { + public C(); + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546198")] @@ -994,17 +1107,19 @@ public async Task TestTypeInFileScopedNamespace2() LanguageNames.CSharp, [metadataSource], languageVersion: "9", fileScopedNamespaces: true); await context.GenerateAndVerifySourceAsync("N.C", - $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -namespace N -{{ - public class [|C|] - {{ - public C(); - }} -}}"); + $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + namespace N + { + public class [|C|] + { + public C(); + } + } + """); } [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546198")] @@ -1016,17 +1131,19 @@ public async Task TestTypeInFileScopedNamespace3() LanguageNames.CSharp, [metadataSource], languageVersion: "10"); await context.GenerateAndVerifySourceAsync("N.C", - $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -namespace N -{{ - public class [|C|] - {{ - public C(); - }} -}}"); + $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + namespace N + { + public class [|C|] + { + public C(); + } + } + """); } [WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546223"), CombinatorialData] @@ -1037,57 +1154,65 @@ public async Task TestInlineConstant(OriginatingProjectLanguage language, bool s var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public const string [|S|] = ""Hello mas""; - - public C(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class C - Public Const [|S|] As String = ""Hello mas"" - - Public Sub New() -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public const string [|S|] = ""Hello mas""; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public const string [|S|] = ""Hello mas""; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public const string [|S|] = "Hello mas"; + + public C(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class C + Public Const [|S|] As String = "Hello mas" + + Public Sub New() + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public const string [|S|] = "Hello mas"; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public const string [|S|] = "Hello mas"; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1097,70 +1222,80 @@ public class C [WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546221"), CombinatorialData] public async Task TestInlineTypeOf(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -using System; + var metadataSource = """ -public class MyTypeAttribute : Attribute -{ - public MyTypeAttribute(Type type) {} -} + using System; + + public class MyTypeAttribute : Attribute + { + public MyTypeAttribute(Type type) {} + } -[MyType(typeof(string))] -public class C {}"; + [MyType(typeof(string))] + public class C {} + """; var symbolName = "C"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -[MyType(typeof(string))] -public class [|C|] -{{ - public C(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - - -Public Class [|C|] - Public Sub New() -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -[MyType(typeof(string))] -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -[MyType(typeof(string))] -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + [MyType(typeof(string))] + public class [|C|] + { + public C(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + + Public Class [|C|] + Public Sub New() + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + [MyType(typeof(string))] + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + [MyType(typeof(string))] + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1175,55 +1310,63 @@ public async Task TestNoDefaultConstructorInStructs(OriginatingProjectLanguage l var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct [|S|] -{{ -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Structure [|S|] -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct [|S|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct [|S|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct [|S|] + { + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Structure [|S|] + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct [|S|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct [|S|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1238,63 +1381,71 @@ public async Task TestReferenceDefinedType(OriginatingProjectLanguage language, var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|C|] -{{ - public C(); - - public static C Create(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class [|C|] - Public Sub New() - - Public Shared Function Create() As C -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ - public static C Create() - {{ - return new C(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|] -{{ - public static C Create() - {{ - return new C(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|C|] + { + public C(); + + public static C Create(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class [|C|] + Public Sub New() + + Public Shared Function Create() As C + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + public static C Create() + { + return new C(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|] + { + public static C Create() + { + return new C(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1309,57 +1460,65 @@ public async Task TestGenericType(OriginatingProjectLanguage language, bool sign var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|G|] -{{ - public SomeType S; - - public G(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class [|G|](Of SomeType) - Public S As SomeType - - Public Sub New() -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|G|] -{{ - public SomeType S; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|G|] -{{ - public SomeType S; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|G|] + { + public SomeType S; + + public G(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class [|G|](Of SomeType) + Public S As SomeType + + Public Sub New() + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|G|] + { + public SomeType S; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|G|] + { + public SomeType S; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1370,73 +1529,83 @@ public class [|G|] [WorkItem("https://github.com/dotnet/roslyn/issues/38916")] public async Task TestParameterAttributes(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public class C<[My] T> -{ - public void Method([My] T x, [My] T y) { } -} + var metadataSource = """ -internal class MyAttribute : System.Attribute { } -"; + public class C<[My] T> + { + public void Method([My] T x, [My] T y) { } + } + + internal class MyAttribute : System.Attribute { } + + """; var symbolName = "C`1"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|C|]<[MyAttribute] T> -{{ - public C(); - - public void Method([MyAttribute] T x, [MyAttribute] T y); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class [|C|](Of T) - Public Sub New() - - Public Sub Method( x As T, y As T) -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|]<[My] T> -{{ - public void Method([My] T x, [My] T y) - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|C|]<[My] T> -{{ - public void Method([My] T x, [My] T y) - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|C|]<[MyAttribute] T> + { + public C(); + + public void Method([MyAttribute] T x, [MyAttribute] T y); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class [|C|](Of T) + Public Sub New() + + Public Sub Method( x As T, y As T) + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|]<[My] T> + { + public void Method([My] T x, [My] T y) + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|C|]<[My] T> + { + public void Method([My] T x, [My] T y) + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1447,66 +1616,76 @@ public void Method([My] T x, [My] T y) [WorkItem("https://github.com/dotnet/roslyn/issues/38916")] public async Task TestGenericWithNullableReferenceTypes(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -#nullable enable -public interface C -{ - bool Equals([AllowNull] T other); -} + var metadataSource = """ + + #nullable enable + public interface C + { + bool Equals([AllowNull] T other); + } -internal class AllowNullAttribute : System.Attribute { } -"; + internal class AllowNullAttribute : System.Attribute { } + + """; var symbolName = "C`1"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public interface [|C|] -{{ - bool Equals([AllowNullAttribute] T other); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Interface [|C|](Of T) - Function Equals( other As T) As Boolean -End Interface", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public interface [|C|] -{{ - bool Equals([AllowNull] T other); -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public interface [|C|] -{{ - bool Equals([AllowNull] T other); -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public interface [|C|] + { + bool Equals([AllowNullAttribute] T other); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Interface [|C|](Of T) + Function Equals( other As T) As Boolean + End Interface + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public interface [|C|] + { + bool Equals([AllowNull] T other); + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public interface [|C|] + { + bool Equals([AllowNull] T other); + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1521,56 +1700,64 @@ public async Task TestGenericDelegate(OriginatingProjectLanguage language, bool var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public C(); - - public delegate void [|D|](SomeType s); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Class C - Public Sub New() - Public Delegate Sub [|D|](Of SomeType)(s As SomeType) -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public delegate void [|D|](SomeType s); -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class C -{{ - public delegate void [|D|](SomeType s); -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public C(); + + public delegate void [|D|](SomeType s); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Class C + Public Sub New() + Public Delegate Sub [|D|](Of SomeType)(s As SomeType) + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public delegate void [|D|](SomeType s); + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class C + { + public delegate void [|D|](SomeType s); + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1580,81 +1767,91 @@ public class C [WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546200"), CombinatorialData] public async Task TestAttribute(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -using System; + var metadataSource = """ -namespace N -{ - public class WorkingAttribute : Attribute - { - public WorkingAttribute(bool working) {} - } -} + using System; + + namespace N + { + public class WorkingAttribute : Attribute + { + public WorkingAttribute(bool working) {} + } + } -[N.Working(true)] -public class C {}"; + [N.Working(true)] + public class C {} + """; var symbolName = "C"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using N; - -[Working(true)] -public class [|C|] -{{ - public C(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports N - - -Public Class [|C|] - Public Sub New() -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using N; - -[Working(true)] -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using N; - -[Working(true)] -public class [|C|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using N; + + [Working(true)] + public class [|C|] + { + public C(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports N + + + Public Class [|C|] + Public Sub New() + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using N; + + [Working(true)] + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using N; + + [Working(true)] + public class [|C|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -1712,6 +1909,7 @@ public async Task TestWorkspaceContextHasReasonableProjectName() { using var context = TestContext.Create(); var compilation = await context.DefaultProject.GetCompilationAsync(); + Assert.NotNull(compilation); var result = await context.GenerateSourceAsync(compilation.ObjectType); var openedDocument = context.GetDocument(result); @@ -1724,7 +1922,7 @@ public async Task TestReuseGenerateFromDifferentProject() { using var context = TestContext.Create(); var projectId = ProjectId.CreateNewId(); - var project = context.CurrentSolution.AddProject(projectId, "ProjectB", "ProjectB", LanguageNames.CSharp).GetProject(projectId) + var project = context.CurrentSolution.AddProject(projectId, "ProjectB", "ProjectB", LanguageNames.CSharp).GetRequiredProject(projectId) .WithMetadataReferences(context.DefaultProject.MetadataReferences) .WithCompilationOptions(new CS.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); @@ -1738,7 +1936,7 @@ public async Task TestNotReusedGeneratingForDifferentLanguage() { using var context = TestContext.Create(LanguageNames.CSharp); var projectId = ProjectId.CreateNewId(); - var project = context.CurrentSolution.AddProject(projectId, "ProjectB", "ProjectB", LanguageNames.VisualBasic).GetProject(projectId) + var project = context.CurrentSolution.AddProject(projectId, "ProjectB", "ProjectB", LanguageNames.VisualBasic).GetRequiredProject(projectId) .WithMetadataReferences(context.DefaultProject.MetadataReferences) .WithCompilationOptions(new VB.VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); @@ -1759,28 +1957,32 @@ public async Task FormatMetadataAsSource() [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530829")] public async Task IndexedProperty() { - var metadataSource = @" -Public Class C - Public Property IndexProp(ByVal p1 As Integer) As String - Get - Return Nothing - End Get - Set(ByVal value As String) - - End Set - End Property -End Class"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class C -{{ - public C(); - - public string [|get_IndexProp|](int p1); - public void set_IndexProp(int p1, string value); -}}"; + var metadataSource = """ + + Public Class C + Public Property IndexProp(ByVal p1 As Integer) As String + Get + Return Nothing + End Get + Set(ByVal value As String) + + End Set + End Property + End Class + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class C + { + public C(); + + public string [|get_IndexProp|](int p1); + public void set_IndexProp(int p1, string value); + } + """; var symbolName = "C.get_IndexProp"; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, expected); } @@ -1788,29 +1990,33 @@ public class C [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/566688")] public async Task AttributeReferencingInternalNestedType() { - var metadataSource = @"using System; -[My(typeof(D))] -public class C -{ - public C() { } - - internal class D { } -} - -public class MyAttribute : Attribute -{ - public MyAttribute(Type t) { } -}"; - - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -[My(typeof(D))] -public class [|C|] -{{ - public C(); -}}"; + var metadataSource = """ + using System; + [My(typeof(D))] + public class C + { + public C() { } + + internal class D { } + } + + public class MyAttribute : Attribute + { + public MyAttribute(Type t) { } + } + """; + + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + [My(typeof(D))] + public class [|C|] + { + public C(); + } + """; var symbolName = "C"; await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, expected); } @@ -1818,304 +2024,314 @@ public class [|C|] [WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530978"), CombinatorialData] public async Task TestAttributesOnMembers(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @"using System; + var metadataSource = """ + using System; -[Obsolete] -public class C -{ - [Obsolete] - [ThreadStatic] - public int field1; + [Obsolete] + public class C + { + [Obsolete] + [ThreadStatic] + public int field1; - [Obsolete] - public int prop1 { get; set; } + [Obsolete] + public int prop1 { get; set; } - [Obsolete] - public int prop2 { get { return 10; } set {} } + [Obsolete] + public int prop2 { get { return 10; } set {} } - [Obsolete] - public void method1() {} + [Obsolete] + public void method1() {} - [Obsolete] - public C() {} + [Obsolete] + public C() {} - [Obsolete] - ~C() {} + [Obsolete] + ~C() {} - [Obsolete] - public int this[int x] { get { return 10; } set {} } + [Obsolete] + public int this[int x] { get { return 10; } set {} } - [Obsolete] - public event Action event1; + [Obsolete] + public event Action event1; - [Obsolete] - public event Action event2 { add {} remove {}} + [Obsolete] + public event Action event2 { add {} remove {}} - public void method2([System.Runtime.CompilerServices.CallerMemberName] string name = """") {} + public void method2([System.Runtime.CompilerServices.CallerMemberName] string name = "") {} - [Obsolete] - public static C operator + (C c1, C c2) { return new C(); } -} -"; + [Obsolete] + public static C operator + (C c1, C c2) { return new C(); } + } + + """; var symbolName = "C"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System; -using System.Reflection; -using System.Runtime.CompilerServices; - -[DefaultMember(""Item"")] -[Obsolete] -public class [|C|] -{{ - [Obsolete] - [ThreadStatic] - public int field1; - - [Obsolete] - public C(); - - [Obsolete] - ~C(); - - [Obsolete] - public int this[int x] {{ get; set; }} - - [Obsolete] - public int prop1 {{ get; set; }} - [Obsolete] - public int prop2 {{ get; set; }} - - [Obsolete] - public event Action event1; - [Obsolete] - public event Action event2; - - [Obsolete] - public void method1(); - public void method2([CallerMemberName] string name = """"); - - [Obsolete] - public static C operator +(C c1, C c2); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System -Imports System.Reflection -Imports System.Runtime.CompilerServices - - -Public Class [|C|] - - Public field1 As Integer - - - Public Sub New() - - - Public Property prop1 As Integer - - Public Property prop2 As Integer - - Default Public Property Item(x As Integer) As Integer - - - Public Event event1 As Action - - Public Event event2 As Action - - - Public Sub method1() - Public Sub method2( Optional name As String = """") - - Protected Overrides Sub Finalize() - - - Public Shared Operator +(c1 As C, c2 As C) As C -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; -using System.Runtime.CompilerServices; - -[Obsolete] -public class [|C|] -{{ - [Obsolete] - [ThreadStatic] - public int field1; - - [Obsolete] - public int prop1 {{ get; set; }} - - [Obsolete] - public int prop2 - {{ - get - {{ - return 10; - }} - set - {{ - }} - }} - - [Obsolete] - public int this[int x] - {{ - get - {{ - return 10; - }} - set - {{ - }} - }} - - [Obsolete] - public event Action event1; - - [Obsolete] - public event Action event2 - {{ - add - {{ - }} - remove - {{ - }} - }} - - [Obsolete] - public void method1() - {{ - }} - - [Obsolete] - public C() - {{ - }} - - [Obsolete] - ~C() - {{ - }} - - public void method2([CallerMemberName] string name = """") - {{ - }} - - [Obsolete] - public static C operator +(C c1, C c2) - {{ - return new C(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; -using System.Runtime.CompilerServices; - -[Obsolete] -public class [|C|] -{{ - [Obsolete] - [ThreadStatic] - public int field1; - - [Obsolete] - public int prop1 {{ get; set; }} - - [Obsolete] - public int prop2 - {{ - get - {{ - return 10; - }} - set - {{ - }} - }} - - [Obsolete] - public int this[int x] - {{ - get - {{ - return 10; - }} - set - {{ - }} - }} - - [Obsolete] - public event Action event1; - - [Obsolete] - public event Action event2 - {{ - add - {{ - }} - remove - {{ - }} - }} - - [Obsolete] - public void method1() - {{ - }} - - [Obsolete] - public C() - {{ - }} - - [Obsolete] - ~C() - {{ - }} - - public void method2([CallerMemberName] string name = """") - {{ - }} - - [Obsolete] - public static C operator +(C c1, C c2) - {{ - return new C(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System; + using System.Reflection; + using System.Runtime.CompilerServices; + + [DefaultMember("Item")] + [Obsolete] + public class [|C|] + { + [Obsolete] + [ThreadStatic] + public int field1; + + [Obsolete] + public C(); + + [Obsolete] + ~C(); + + [Obsolete] + public int this[int x] { get; set; } + + [Obsolete] + public int prop1 { get; set; } + [Obsolete] + public int prop2 { get; set; } + + [Obsolete] + public event Action event1; + [Obsolete] + public event Action event2; + + [Obsolete] + public void method1(); + public void method2([CallerMemberName] string name = ""); + + [Obsolete] + public static C operator +(C c1, C c2); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + Imports System.Reflection + Imports System.Runtime.CompilerServices + + + Public Class [|C|] + + Public field1 As Integer + + + Public Sub New() + + + Public Property prop1 As Integer + + Public Property prop2 As Integer + + Default Public Property Item(x As Integer) As Integer + + + Public Event event1 As Action + + Public Event event2 As Action + + + Public Sub method1() + Public Sub method2( Optional name As String = "") + + Protected Overrides Sub Finalize() + + + Public Shared Operator +(c1 As C, c2 As C) As C + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + using System.Runtime.CompilerServices; + + [Obsolete] + public class [|C|] + { + [Obsolete] + [ThreadStatic] + public int field1; + + [Obsolete] + public int prop1 { get; set; } + + [Obsolete] + public int prop2 + { + get + { + return 10; + } + set + { + } + } + + [Obsolete] + public int this[int x] + { + get + { + return 10; + } + set + { + } + } + + [Obsolete] + public event Action event1; + + [Obsolete] + public event Action event2 + { + add + { + } + remove + { + } + } + + [Obsolete] + public void method1() + { + } + + [Obsolete] + public C() + { + } + + [Obsolete] + ~C() + { + } + + public void method2([CallerMemberName] string name = "") + { + } + + [Obsolete] + public static C operator +(C c1, C c2) + { + return new C(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + using System.Runtime.CompilerServices; + + [Obsolete] + public class [|C|] + { + [Obsolete] + [ThreadStatic] + public int field1; + + [Obsolete] + public int prop1 { get; set; } + + [Obsolete] + public int prop2 + { + get + { + return 10; + } + set + { + } + } + + [Obsolete] + public int this[int x] + { + get + { + return 10; + } + set + { + } + } + + [Obsolete] + public event Action event1; + + [Obsolete] + public event Action event2 + { + add + { + } + remove + { + } + } + + [Obsolete] + public void method1() + { + } + + [Obsolete] + public C() + { + } + + [Obsolete] + ~C() + { + } + + public void method2([CallerMemberName] string name = "") + { + } + + [Obsolete] + public static C operator +(C c1, C c2) + { + return new C(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -2125,248 +2341,258 @@ public void method2([CallerMemberName] string name = """") [WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530923"), CombinatorialData] public async Task TestEmptyLineBetweenMembers(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @"using System; - -public class C -{ - public int field1; - public int prop1 { get; set; } - public int field2; - public int prop2 { get { return 10; } set {} } - public void method1() {} - public C() {} - public void method2([System.Runtime.CompilerServices.CallerMemberName] string name = """") {} - ~C() {} - public int this[int x] { get { return 10; } set {} } - public event Action event1; - public static C operator + (C c1, C c2) { return new C(); } - public event Action event2 { add {} remove {}} - public static C operator - (C c1, C c2) { return new C(); } -} -"; + var metadataSource = """ + using System; + + public class C + { + public int field1; + public int prop1 { get; set; } + public int field2; + public int prop2 { get { return 10; } set {} } + public void method1() {} + public C() {} + public void method2([System.Runtime.CompilerServices.CallerMemberName] string name = "") {} + ~C() {} + public int this[int x] { get { return 10; } set {} } + public event Action event1; + public static C operator + (C c1, C c2) { return new C(); } + public event Action event2 { add {} remove {}} + public static C operator - (C c1, C c2) { return new C(); } + } + + """; var symbolName = "C"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System; -using System.Reflection; -using System.Runtime.CompilerServices; - -[DefaultMember(""Item"")] -public class [|C|] -{{ - public int field1; - public int field2; - - public C(); - - ~C(); - - public int this[int x] {{ get; set; }} - - public int prop1 {{ get; set; }} - public int prop2 {{ get; set; }} - - public event Action event1; - public event Action event2; - - public void method1(); - public void method2([CallerMemberName] string name = """"); - - public static C operator +(C c1, C c2); - public static C operator -(C c1, C c2); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System -Imports System.Reflection -Imports System.Runtime.CompilerServices - - -Public Class [|C|] - Public field1 As Integer - Public field2 As Integer - - Public Sub New() - - Public Property prop1 As Integer - Public Property prop2 As Integer - Default Public Property Item(x As Integer) As Integer - - Public Event event1 As Action - Public Event event2 As Action - - Public Sub method1() - Public Sub method2( Optional name As String = """") - Protected Overrides Sub Finalize() - - Public Shared Operator +(c1 As C, c2 As C) As C - Public Shared Operator -(c1 As C, c2 As C) As C -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; -using System.Runtime.CompilerServices; - -public class [|C|] -{{ - public int field1; - - public int field2; - - public int prop1 {{ get; set; }} - - public int prop2 - {{ - get - {{ - return 10; - }} - set - {{ - }} - }} - - public int this[int x] - {{ - get - {{ - return 10; - }} - set - {{ - }} - }} - - public event Action event1; - - public event Action event2 - {{ - add - {{ - }} - remove - {{ - }} - }} - - public void method1() - {{ - }} - - public void method2([CallerMemberName] string name = """") - {{ - }} - - ~C() - {{ - }} - - public static C operator +(C c1, C c2) - {{ - return new C(); - }} - - public static C operator -(C c1, C c2) - {{ - return new C(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; -using System.Runtime.CompilerServices; - -public class [|C|] -{{ - public int field1; - - public int field2; - - public int prop1 {{ get; set; }} - - public int prop2 - {{ - get - {{ - return 10; - }} - set - {{ - }} - }} - - public int this[int x] - {{ - get - {{ - return 10; - }} - set - {{ - }} - }} - - public event Action event1; - - public event Action event2 - {{ - add - {{ - }} - remove - {{ - }} - }} - - public void method1() - {{ - }} - - public void method2([CallerMemberName] string name = """") - {{ - }} - - ~C() - {{ - }} - - public static C operator +(C c1, C c2) - {{ - return new C(); - }} - - public static C operator -(C c1, C c2) - {{ - return new C(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System; + using System.Reflection; + using System.Runtime.CompilerServices; + + [DefaultMember("Item")] + public class [|C|] + { + public int field1; + public int field2; + + public C(); + + ~C(); + + public int this[int x] { get; set; } + + public int prop1 { get; set; } + public int prop2 { get; set; } + + public event Action event1; + public event Action event2; + + public void method1(); + public void method2([CallerMemberName] string name = ""); + + public static C operator +(C c1, C c2); + public static C operator -(C c1, C c2); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + Imports System.Reflection + Imports System.Runtime.CompilerServices + + + Public Class [|C|] + Public field1 As Integer + Public field2 As Integer + + Public Sub New() + + Public Property prop1 As Integer + Public Property prop2 As Integer + Default Public Property Item(x As Integer) As Integer + + Public Event event1 As Action + Public Event event2 As Action + + Public Sub method1() + Public Sub method2( Optional name As String = "") + Protected Overrides Sub Finalize() + + Public Shared Operator +(c1 As C, c2 As C) As C + Public Shared Operator -(c1 As C, c2 As C) As C + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + using System.Runtime.CompilerServices; + + public class [|C|] + { + public int field1; + + public int field2; + + public int prop1 { get; set; } + + public int prop2 + { + get + { + return 10; + } + set + { + } + } + + public int this[int x] + { + get + { + return 10; + } + set + { + } + } + + public event Action event1; + + public event Action event2 + { + add + { + } + remove + { + } + } + + public void method1() + { + } + + public void method2([CallerMemberName] string name = "") + { + } + + ~C() + { + } + + public static C operator +(C c1, C c2) + { + return new C(); + } + + public static C operator -(C c1, C c2) + { + return new C(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + using System.Runtime.CompilerServices; + + public class [|C|] + { + public int field1; + + public int field2; + + public int prop1 { get; set; } + + public int prop2 + { + get + { + return 10; + } + set + { + } + } + + public int this[int x] + { + get + { + return 10; + } + set + { + } + } + + public event Action event1; + + public event Action event2 + { + add + { + } + remove + { + } + } + + public void method1() + { + } + + public void method2([CallerMemberName] string name = "") + { + } + + ~C() + { + } + + public static C operator +(C c1, C c2) + { + return new C(); + } + + public static C operator -(C c1, C c2) + { + return new C(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -2376,111 +2602,121 @@ public void method2([CallerMemberName] string name = """") [WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/728644"), CombinatorialData] public async Task TestEmptyLineBetweenMembers2(OriginatingProjectLanguage language, bool signaturesOnly) { - var source = @" -using System; + var source = """ -/// T:IGoo -public interface IGoo -{ - /// P:IGoo.Prop1 - Uri Prop1 { get; set; } - /// M:IGoo.Method1 - Uri Method1(); -} -"; + using System; + + /// T:IGoo + public interface IGoo + { + /// P:IGoo.Prop1 + Uri Prop1 { get; set; } + /// M:IGoo.Method1 + Uri Method1(); + } + + """; var symbolName = "IGoo"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System; - -// -// {FeaturesResources.Summary_colon} -// T:IGoo -public interface [|IGoo|] -{{ - // - // {FeaturesResources.Summary_colon} - // P:IGoo.Prop1 - Uri Prop1 {{ get; set; }} - - // - // {FeaturesResources.Summary_colon} - // M:IGoo.Method1 - Uri Method1(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System - -' -' {FeaturesResources.Summary_colon} -' T:IGoo -Public Interface [|IGoo|] - ' - ' {FeaturesResources.Summary_colon} - ' P:IGoo.Prop1 - Property Prop1 As Uri - - ' - ' {FeaturesResources.Summary_colon} - ' M:IGoo.Method1 - Function Method1() As Uri -End Interface", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; - -public interface [|IGoo|] -{{ - Uri Prop1 {{ get; set; }} - - Uri Method1(); -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} ------------------- -{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; - -public interface [|IGoo|] -{{ - Uri Prop1 {{ get; set; }} - - Uri Method1(); -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} ------------------- -{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System; + + // + // {{FeaturesResources.Summary_colon}} + // T:IGoo + public interface [|IGoo|] + { + // + // {{FeaturesResources.Summary_colon}} + // P:IGoo.Prop1 + Uri Prop1 { get; set; } + + // + // {{FeaturesResources.Summary_colon}} + // M:IGoo.Method1 + Uri Method1(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + + ' + ' {FeaturesResources.Summary_colon} + ' T:IGoo + Public Interface [|IGoo|] + ' + ' {FeaturesResources.Summary_colon} + ' P:IGoo.Prop1 + Property Prop1 As Uri + + ' + ' {FeaturesResources.Summary_colon} + ' M:IGoo.Method1 + Function Method1() As Uri + End Interface + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + public interface [|IGoo|] + { + Uri Prop1 { get; set; } + + Uri Method1(); + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + public interface [|IGoo|] + { + Uri Prop1 { get; set; } + + Uri Method1(); + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -2490,81 +2726,91 @@ public interface [|IGoo|] [WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/679114"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/715013"), CombinatorialData] public async Task TestDefaultValueEnum(OriginatingProjectLanguage language, bool signaturesOnly) { - var source = @" -using System.IO; + var source = """ -public class Test -{ - public void goo(FileOptions options = 0) {} -} -"; + using System.IO; + + public class Test + { + public void goo(FileOptions options = 0) {} + } + + """; var symbolName = "Test"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System.IO; - -public class [|Test|] -{{ - public Test(); - - public void goo(FileOptions options = FileOptions.None); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System.IO - -Public Class [|Test|] - Public Sub New() - - Public Sub goo(Optional options As FileOptions = FileOptions.None) -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.IO; - -public class [|Test|] -{{ - public void goo(FileOptions options = FileOptions.None) - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.IO; - -public class [|Test|] -{{ - public void goo(FileOptions options = FileOptions.None) - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System.IO; + + public class [|Test|] + { + public Test(); + + public void goo(FileOptions options = FileOptions.None); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System.IO + + Public Class [|Test|] + Public Sub New() + + Public Sub goo(Optional options As FileOptions = FileOptions.None) + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.IO; + + public class [|Test|] + { + public void goo(FileOptions options = FileOptions.None) + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.IO; + + public class [|Test|] + { + public void goo(FileOptions options = FileOptions.None) + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -2574,85 +2820,95 @@ public void goo(FileOptions options = FileOptions.None) [WpfTheory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/651261"), CombinatorialData] public async Task TestNullAttribute(OriginatingProjectLanguage language, bool signaturesOnly) { - var source = @" -using System; + var source = """ -[Test(null)] -public class TestAttribute : Attribute -{ - public TestAttribute(int[] i) - { - } -}"; + using System; + + [Test(null)] + public class TestAttribute : Attribute + { + public TestAttribute(int[] i) + { + } + } + """; var symbolName = "TestAttribute"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System; - -[Test(null)] -public class [|TestAttribute|] : Attribute -{{ - public TestAttribute(int[] i); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System - - -Public Class [|TestAttribute|] - Inherits Attribute - - Public Sub New(i() As Integer) -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; - -[Test(null)] -public class [|TestAttribute|] : Attribute -{{ - public TestAttribute(int[] i) - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; - -[Test(null)] -public class [|TestAttribute|] : Attribute -{{ - public TestAttribute(int[] i) - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System; + + [Test(null)] + public class [|TestAttribute|] : Attribute + { + public TestAttribute(int[] i); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + + + Public Class [|TestAttribute|] + Inherits Attribute + + Public Sub New(i() As Integer) + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + [Test(null)] + public class [|TestAttribute|] : Attribute + { + public TestAttribute(int[] i) + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + [Test(null)] + public class [|TestAttribute|] : Attribute + { + public TestAttribute(int[] i) + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -2662,27 +2918,33 @@ public TestAttribute(int[] i) [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/897006")] public async Task TestNavigationViaReducedExtensionMethodCS() { - var metadata = @"using System; -public static class ObjectExtensions -{ - public static void M(this object o, int x) { } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - new object().[|M|](5); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public static class ObjectExtensions -{{ - public static void [|M|](this object o, int x); -}}"; + var metadata = """ + using System; + public static class ObjectExtensions + { + public static void M(this object o, int x) { } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + new object().[|M|](5); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public static class ObjectExtensions + { + public static void [|M|](this object o, int x); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -2697,34 +2959,40 @@ public static class ObjectExtensions [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/897006")] public async Task TestNavigationViaReducedExtensionMethodVB() { - var metadata = @"Imports System.Runtime.CompilerServices -Namespace NS - Public Module StringExtensions - - Public Sub M(ByVal o As String, x As Integer) - End Sub - End Module -End Namespace"; - var sourceWithSymbolReference = @" -Imports NS.StringExtensions -Public Module C - Sub M() - Dim s = ""Yay"" - s.[|M|](1) - End Sub -End Module"; - var expected = $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System.Runtime.CompilerServices - -Namespace NS - - Public Module StringExtensions - Public Sub [|M|](o As String, x As Integer) - End Module -End Namespace"; + var metadata = """ + Imports System.Runtime.CompilerServices + Namespace NS + Public Module StringExtensions + + Public Sub M(ByVal o As String, x As Integer) + End Sub + End Module + End Namespace + """; + var sourceWithSymbolReference = """ + + Imports NS.StringExtensions + Public Module C + Sub M() + Dim s = "Yay" + s.[|M|](1) + End Sub + End Module + """; + var expected = $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System.Runtime.CompilerServices + + Namespace NS + + Public Module StringExtensions + Public Sub [|M|](o As String, x As Integer) + End Module + End Namespace + """; using var context = TestContext.Create( LanguageNames.VisualBasic, @@ -2739,118 +3007,128 @@ End Module [WpfTheory, CombinatorialData] public async Task TestIndexersAndOperators(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @"public class Program -{ - public int this[int x] - { - get - { - return 0; - } - set - { - - } - } - - public static Program operator + (Program p1, Program p2) - { - return new Program(); - } -}"; + var metadataSource = """ + public class Program + { + public int this[int x] + { + get + { + return 0; + } + set + { + + } + } + + public static Program operator + (Program p1, Program p2) + { + return new Program(); + } + } + """; var symbolName = "Program"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System.Reflection; - -[DefaultMember(""Item"")] -public class [|Program|] -{{ - public Program(); - - public int this[int x] {{ get; set; }} - - public static Program operator +(Program p1, Program p2); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System.Reflection - - -Public Class [|Program|] - Public Sub New() - - Default Public Property Item(x As Integer) As Integer - - Public Shared Operator +(p1 As Program, p2 As Program) As Program -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|Program|] -{{ - public int this[int x] - {{ - get - {{ - return 0; - }} - set - {{ - }} - }} - - public static Program operator +(Program p1, Program p2) - {{ - return new Program(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public class [|Program|] -{{ - public int this[int x] - {{ - get - {{ - return 0; - }} - set - {{ - }} - }} - - public static Program operator +(Program p1, Program p2) - {{ - return new Program(); - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System.Reflection; + + [DefaultMember("Item")] + public class [|Program|] + { + public Program(); + + public int this[int x] { get; set; } + + public static Program operator +(Program p1, Program p2); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System.Reflection + + + Public Class [|Program|] + Public Sub New() + + Default Public Property Item(x As Integer) As Integer + + Public Shared Operator +(p1 As Program, p2 As Program) As Program + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|Program|] + { + public int this[int x] + { + get + { + return 0; + } + set + { + } + } + + public static Program operator +(Program p1, Program p2) + { + return new Program(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public class [|Program|] + { + public int this[int x] + { + get + { + return 0; + } + set + { + } + } + + public static Program operator +(Program p1, Program p2) + { + return new Program(); + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -2860,103 +3138,113 @@ public int this[int x] [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/15387"), CombinatorialData] public async Task TestComImport1(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -using System.Runtime.InteropServices; + var metadataSource = """ -[ComImport] -[Guid(""666A175D-2448-447A-B786-CCC82CBEF156"")] -public interface IComImport -{ - void MOverload(); - void X(); - void MOverload(int i); - int Prop { get; } -}"; + using System.Runtime.InteropServices; + + [ComImport] + [Guid("666A175D-2448-447A-B786-CCC82CBEF156")] + public interface IComImport + { + void MOverload(); + void X(); + void MOverload(int i); + int Prop { get; } + } + """; var symbolName = "IComImport"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System.Runtime.InteropServices; - -[Guid(""666A175D-2448-447A-B786-CCC82CBEF156"")] -public interface [|IComImport|] -{{ - void MOverload(); - void X(); - void MOverload(int i); - - int Prop {{ get; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System.Runtime.InteropServices - - -Public Interface [|IComImport|] - ReadOnly Property Prop As Integer - - Sub MOverload() - Sub X() - Sub MOverload(i As Integer) -End Interface", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[ComImport] -[Guid(""666A175D-2448-447A-B786-CCC82CBEF156"")] -public interface [|IComImport|] -{{ - void MOverload(); - - void X(); - - void MOverload(int i); - - int Prop {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[ComImport] -[Guid(""666A175D-2448-447A-B786-CCC82CBEF156"")] -public interface [|IComImport|] -{{ - void MOverload(); - - void X(); - - void MOverload(int i); - - int Prop {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System.Runtime.InteropServices; + + [Guid("666A175D-2448-447A-B786-CCC82CBEF156")] + public interface [|IComImport|] + { + void MOverload(); + void X(); + void MOverload(int i); + + int Prop { get; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System.Runtime.InteropServices + + + Public Interface [|IComImport|] + ReadOnly Property Prop As Integer + + Sub MOverload() + Sub X() + Sub MOverload(i As Integer) + End Interface + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [ComImport] + [Guid("666A175D-2448-447A-B786-CCC82CBEF156")] + public interface [|IComImport|] + { + void MOverload(); + + void X(); + + void MOverload(int i); + + int Prop { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [ComImport] + [Guid("666A175D-2448-447A-B786-CCC82CBEF156")] + public interface [|IComImport|] + { + void MOverload(); + + void X(); + + void MOverload(int i); + + int Prop { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -2966,79 +3254,89 @@ public interface [|IComImport|] [WpfTheory, CombinatorialData] public async Task TestOptionalParameterWithDefaultLiteral(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -using System.Threading; + var metadataSource = """ -public class C { - public void M(CancellationToken cancellationToken = default(CancellationToken)) { } -}"; + using System.Threading; + + public class C { + public void M(CancellationToken cancellationToken = default(CancellationToken)) { } + } + """; var symbolName = "C"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System.Threading; - -public class [|C|] -{{ - public C(); - - public void M(CancellationToken cancellationToken = default); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System.Threading - -Public Class [|C|] - Public Sub New() - - Public Sub M(Optional cancellationToken As CancellationToken = Nothing) -End Class", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Threading; - -public class [|C|] -{{ - public void M(CancellationToken cancellationToken = default(CancellationToken)) - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Threading; - -public class [|C|] -{{ - public void M(CancellationToken cancellationToken = default(CancellationToken)) - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System.Threading; + + public class [|C|] + { + public C(); + + public void M(CancellationToken cancellationToken = default); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System.Threading + + Public Class [|C|] + Public Sub New() + + Public Sub M(Optional cancellationToken As CancellationToken = Nothing) + End Class + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Threading; + + public class [|C|] + { + public void M(CancellationToken cancellationToken = default(CancellationToken)) + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Threading; + + public class [|C|] + { + public void M(CancellationToken cancellationToken = default(CancellationToken)) + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3055,111 +3353,139 @@ public class [|C|] [WpfTheory, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems?id=446567"), CombinatorialData] public async Task TestDocCommentsWithUnixNewLine(OriginatingProjectLanguage language, bool signaturesOnly) { - var source = @" -using System; + var source = """ -/// T:IGoo" + "\n/// ABCDE\n" + @"/// FGHIJK -public interface IGoo -{ - /// P:IGoo.Prop1" + "\n/// ABCDE\n" + @"/// FGHIJK - Uri Prop1 { get; set; } - /// M:IGoo.Method1" + "\n/// ABCDE\n" + @"/// FGHIJK - Uri Method1(); -} -"; - var symbolName = "IGoo"; + using System; - var expected = (language, signaturesOnly) switch - { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + /// T:IGoo + """ + """ -using System; + /// ABCDE -// -// {FeaturesResources.Summary_colon} -// T:IGoo ABCDE FGHIJK -public interface [|IGoo|] -{{ - // - // {FeaturesResources.Summary_colon} - // P:IGoo.Prop1 ABCDE FGHIJK - Uri Prop1 {{ get; set; }} - - // - // {FeaturesResources.Summary_colon} - // M:IGoo.Method1 ABCDE FGHIJK - Uri Method1(); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System - -' -' {FeaturesResources.Summary_colon} -' T:IGoo ABCDE FGHIJK -Public Interface [|IGoo|] - ' - ' {FeaturesResources.Summary_colon} - ' P:IGoo.Prop1 ABCDE FGHIJK - Property Prop1 As Uri - - ' - ' {FeaturesResources.Summary_colon} - ' M:IGoo.Method1 ABCDE FGHIJK - Function Method1() As Uri -End Interface", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion + """ + """ + /// FGHIJK + public interface IGoo + { + /// P:IGoo.Prop1 + """ + """ -using System; + /// ABCDE -public interface [|IGoo|] -{{ - Uri Prop1 {{ get; set; }} - - Uri Method1(); -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} ------------------- -{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion + """ + """ + /// FGHIJK + Uri Prop1 { get; set; } + /// M:IGoo.Method1 + """ + """ -using System; + /// ABCDE -public interface [|IGoo|] -{{ - Uri Prop1 {{ get; set; }} - - Uri Method1(); -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} ------------------- -{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")} -#endif", + """ + """ + /// FGHIJK + Uri Method1(); + } + + """; + var symbolName = "IGoo"; + + var expected = (language, signaturesOnly) switch + { + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System; + + // + // {{FeaturesResources.Summary_colon}} + // T:IGoo ABCDE FGHIJK + public interface [|IGoo|] + { + // + // {{FeaturesResources.Summary_colon}} + // P:IGoo.Prop1 ABCDE FGHIJK + Uri Prop1 { get; set; } + + // + // {{FeaturesResources.Summary_colon}} + // M:IGoo.Method1 ABCDE FGHIJK + Uri Method1(); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + + ' + ' {FeaturesResources.Summary_colon} + ' T:IGoo ABCDE FGHIJK + Public Interface [|IGoo|] + ' + ' {FeaturesResources.Summary_colon} + ' P:IGoo.Prop1 ABCDE FGHIJK + Property Prop1 As Uri + + ' + ' {FeaturesResources.Summary_colon} + ' M:IGoo.Method1 ABCDE FGHIJK + Function Method1() As Uri + End Interface + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + public interface [|IGoo|] + { + Uri Prop1 { get; set; } + + Uri Method1(); + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + + public interface [|IGoo|] + { + Uri Prop1 { get; set; } + + Uri Method1(); + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "System.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3169,26 +3495,32 @@ public interface [|IGoo|] [WpfFact] public async Task TestUnmanagedCSharpConstraint_Type() { - var metadata = @"using System; -public class TestType where T : unmanaged -{ -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new [|TestType|]<int>(); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|TestType|] where T : unmanaged -{{ - public TestType(); -}}"; + var metadata = """ + using System; + public class TestType where T : unmanaged + { + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new [|TestType|]<int>(); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|TestType|] where T : unmanaged + { + public TestType(); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -3204,31 +3536,37 @@ public class [|TestType|] where T : unmanaged [WpfFact] public async Task TestUnmanagedCSharpConstraint_Method() { - var metadata = @"using System; -public class TestType -{ - public void M() where T : unmanaged - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M|]<int>(); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class TestType -{{ - public TestType(); - - public void [|M|]() where T : unmanaged; -}}"; + var metadata = """ + using System; + public class TestType + { + public void M() where T : unmanaged + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M|]<int>(); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class TestType + { + public TestType(); + + public void [|M|]() where T : unmanaged; + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -3244,20 +3582,26 @@ public class TestType [WpfFact] public async Task TestUnmanagedCSharpConstraint_Delegate() { - var metadata = @"using System; -public delegate void D() where T : unmanaged;"; - var sourceWithSymbolReference = @" -class C -{ - void M([|D|]<int> lambda) - { - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public delegate void [|D|]() where T : unmanaged;"; + var metadata = """ + using System; + public delegate void D() where T : unmanaged; + """; + var sourceWithSymbolReference = """ + + class C + { + void M([|D|]<int> lambda) + { + } + } + """; + var expected = $""" + #region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {CodeAnalysisResources.InMemoryAssembly} + #endregion + + public delegate void [|D|]() where T : unmanaged; + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -3273,11 +3617,13 @@ class C [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/29786")] public async Task TestSByteMinValue() { - var source = @" -class C -{ - sbyte Goo = sbyte.[|MinValue|]; -}"; + var source = """ + + class C + { + sbyte Goo = sbyte.[|MinValue|]; + } + """; var expected = "public const SByte MinValue = -128;"; @@ -3287,10 +3633,12 @@ class C [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/29786")] public async Task TestSByteMinValueVB() { - var source = @" -Class C - Public Goo = SByte.[|MinValue|] -End Class"; + var source = """ + + Class C + Public Goo = SByte.[|MinValue|] + End Class + """; var expected = "Public Const MinValue As [SByte] = -128"; @@ -3300,11 +3648,13 @@ Class C [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt16MinValue() { - var source = @" -class C -{ - short Goo = short.[|MinValue|]; -}"; + var source = """ + + class C + { + short Goo = short.[|MinValue|]; + } + """; var expected = $"public const Int16 MinValue = -32768;"; @@ -3314,10 +3664,12 @@ class C [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt16MinValueVB() { - var source = @" -Class C - Public Goo = Short.[|MinValue|] -End Class"; + var source = """ + + Class C + Public Goo = Short.[|MinValue|] + End Class + """; var expected = $"Public Const MinValue As Int16 = -32768"; @@ -3327,11 +3679,13 @@ Class C [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt32MinValue() { - var source = @" -class C -{ - int Goo = int.[|MinValue|]; -}"; + var source = """ + + class C + { + int Goo = int.[|MinValue|]; + } + """; var expected = $"public const Int32 MinValue = -2147483648;"; @@ -3341,10 +3695,12 @@ class C [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt32MinValueVB() { - var source = @" -Class C - Public Goo = Integer.[|MinValue|] -End Class"; + var source = """ + + Class C + Public Goo = Integer.[|MinValue|] + End Class + """; var expected = $"Public Const MinValue As Int32 = -2147483648"; @@ -3354,11 +3710,13 @@ Class C [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt64MinValue() { - var source = @" -class C -{ - long Goo = long.[|MinValue|]; -}"; + var source = """ + + class C + { + long Goo = long.[|MinValue|]; + } + """; var expected = $"public const Int64 MinValue = -9223372036854775808;"; @@ -3368,10 +3726,12 @@ class C [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/29786")] public async Task TestInt64MinValueVB() { - var source = @" -Class C - Public Goo = Long.[|MinValue|] -End Class"; + var source = """ + + Class C + Public Goo = Long.[|MinValue|] + End Class + """; var expected = $"Public Const MinValue As Int64 = -9223372036854775808"; @@ -3381,65 +3741,75 @@ Class C [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyStruct_ReadOnlyField(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public readonly struct S -{ - public readonly int i; -} -"; + var metadataSource = """ + + public readonly struct S + { + public readonly int i; + } + + """; var symbolName = "S"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public readonly struct [|S|] -{{ - public readonly int i; -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - - - -Public Structure [|S|] - Public ReadOnly i As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public readonly struct [|S|] -{{ - public readonly int i; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public readonly struct [|S|] -{{ - public readonly int i; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public readonly struct [|S|] + { + public readonly int i; + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + + + Public Structure [|S|] + Public ReadOnly i As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public readonly struct [|S|] + { + public readonly int i; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public readonly struct [|S|] + { + public readonly int i; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3449,63 +3819,73 @@ public readonly struct [|S|] [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStruct_ReadOnlyField(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public readonly int i; -} -"; + var metadataSource = """ + + public struct S + { + public readonly int i; + } + + """; var symbolName = "S"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct [|S|] -{{ - public readonly int i; -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Structure [|S|] - Public ReadOnly i As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public struct [|S|] -{{ - public readonly int i; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public struct [|S|] -{{ - public readonly int i; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct [|S|] + { + public readonly int i; + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Structure [|S|] + Public ReadOnly i As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public struct [|S|] + { + public readonly int i; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public struct [|S|] + { + public readonly int i; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3515,67 +3895,77 @@ public struct [|S|] [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestRefStruct(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public ref struct S -{ -} -"; + var metadataSource = """ + + public ref struct S + { + } + + """; var symbolName = "S"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public ref struct [|S|] -{{ -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System - - -Public Structure [|S|] -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public ref struct [|S|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public ref struct [|S|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public ref struct [|S|] + { + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + + + Public Structure [|S|] + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public ref struct [|S|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public ref struct [|S|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3585,67 +3975,77 @@ public ref struct [|S|] [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyRefStruct(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public readonly ref struct S -{ -} -"; + var metadataSource = """ + + public readonly ref struct S + { + } + + """; var symbolName = "S"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public readonly ref struct [|S|] -{{ -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System - - -Public Structure [|S|] -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public readonly ref struct [|S|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public readonly ref struct [|S|] -{{ -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public readonly ref struct [|S|] + { + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + + + Public Structure [|S|] + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public readonly ref struct [|S|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public readonly ref struct [|S|] + { + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3655,74 +4055,84 @@ public readonly ref struct [|S|] [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyMethod(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public readonly void M() {} -} -"; + var metadataSource = """ + + public struct S + { + public readonly void M() {} + } + + """; var symbolName = "S.M"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct S -{{ - public readonly void [|M|](); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - - -Public Structure S - Public Sub [|M|]() -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public readonly void [|M|]() - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public readonly void [|M|]() - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct S + { + public readonly void [|M|](); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + + Public Structure S + Public Sub [|M|]() + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public readonly void [|M|]() + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public readonly void [|M|]() + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3732,75 +4142,85 @@ public struct S [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyMethod_InReadOnlyStruct(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public readonly struct S -{ - public void M() {} -} -"; + var metadataSource = """ + + public readonly struct S + { + public void M() {} + } + + """; var symbolName = "S.M"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public readonly struct S -{{ - public void [|M|](); -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - - - -Public Structure S - Public Sub [|M|]() -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public readonly struct S -{{ - public void [|M|]() - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public readonly struct S -{{ - public void [|M|]() - {{ - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public readonly struct S + { + public void [|M|](); + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + + + Public Structure S + Public Sub [|M|]() + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public readonly struct S + { + public void [|M|]() + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public readonly struct S + { + public void [|M|]() + { + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3810,63 +4230,73 @@ public readonly struct S [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_ReadOnly(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public int P { get; } -} -"; + var metadataSource = """ + + public struct S + { + public int P { get; } + } + + """; var symbolName = "S.P"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct S -{{ - public readonly int [|P|] {{ get; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Structure S - Public ReadOnly Property [|P|] As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public struct S -{{ - public int [|P|] {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public struct S -{{ - public int [|P|] {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct S + { + public readonly int [|P|] { get; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Structure S + Public ReadOnly Property [|P|] As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public struct S + { + public int [|P|] { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public struct S + { + public int [|P|] { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3876,63 +4306,73 @@ public struct S [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_ReadOnly_CSharp7_3(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public int P { get; } -} -"; + var metadataSource = """ + + public struct S + { + public int P { get; } + } + + """; var symbolName = "S.P"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct S -{{ - public int [|P|] {{ get; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Structure S - Public ReadOnly Property [|P|] As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public struct S -{{ - public int [|P|] {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public struct S -{{ - public int [|P|] {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct S + { + public int [|P|] { get; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Structure S + Public ReadOnly Property [|P|] As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public struct S + { + public int [|P|] { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public struct S + { + public int [|P|] { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -3949,63 +4389,73 @@ public struct S [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_ReadOnlyGet(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public readonly int P { get; } -} -"; + var metadataSource = """ + + public struct S + { + public readonly int P { get; } + } + + """; var symbolName = "S.P"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct S -{{ - public readonly int [|P|] {{ get; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Structure S - Public ReadOnly Property [|P|] As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public struct S -{{ - public int [|P|] {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public struct S -{{ - public int [|P|] {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct S + { + public readonly int [|P|] { get; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Structure S + Public ReadOnly Property [|P|] As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public struct S + { + public int [|P|] { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public struct S + { + public int [|P|] { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4015,65 +4465,75 @@ public struct S [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyStructProperty_ReadOnlyGet(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public readonly struct S -{ - public readonly int P { get; } -} -"; + var metadataSource = """ + + public readonly struct S + { + public readonly int P { get; } + } + + """; var symbolName = "S.P"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public readonly struct S -{{ - public int [|P|] {{ get; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - - - -Public Structure S - Public ReadOnly Property [|P|] As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public readonly struct S -{{ - public int [|P|] {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -public readonly struct S -{{ - public int [|P|] {{ get; }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public readonly struct S + { + public int [|P|] { get; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + + + Public Structure S + Public ReadOnly Property [|P|] As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public readonly struct S + { + public int [|P|] { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + public readonly struct S + { + public int [|P|] { get; } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4083,87 +4543,97 @@ public readonly struct S [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_ReadOnlyGet_Set(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public int P { readonly get => 123; set {} } -} -"; + var metadataSource = """ + + public struct S + { + public int P { readonly get => 123; set {} } + } + + """; var symbolName = "S.P"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct S -{{ - public int [|P|] {{ readonly get; set; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Structure S - Public Property [|P|] As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public int [|P|] - {{ - readonly get - {{ - return 123; - }} - set - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public int [|P|] - {{ - readonly get - {{ - return 123; - }} - set - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct S + { + public int [|P|] { readonly get; set; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Structure S + Public Property [|P|] As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public int [|P|] + { + readonly get + { + return 123; + } + set + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public int [|P|] + { + readonly get + { + return 123; + } + set + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4173,87 +4643,97 @@ readonly get [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_Get_ReadOnlySet(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public int P { get => 123; readonly set {} } -} -"; + var metadataSource = """ + + public struct S + { + public int P { get => 123; readonly set {} } + } + + """; var symbolName = "S.P"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct S -{{ - public int [|P|] {{ get; readonly set; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Structure S - Public Property [|P|] As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public int [|P|] - {{ - get - {{ - return 123; - }} - readonly set - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public int [|P|] - {{ - get - {{ - return 123; - }} - readonly set - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct S + { + public int [|P|] { get; readonly set; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Structure S + Public Property [|P|] As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public int [|P|] + { + get + { + return 123; + } + readonly set + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public int [|P|] + { + get + { + return 123; + } + readonly set + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4263,87 +4743,97 @@ readonly set [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructProperty_ReadOnlyGet_ReadOnlySet(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public readonly int P { get => 123; set {} } -} -"; + var metadataSource = """ + + public struct S + { + public readonly int P { get => 123; set {} } + } + + """; var symbolName = "S.P"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public struct S -{{ - public readonly int [|P|] {{ get; set; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Public Structure S - Public Property [|P|] As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public readonly int [|P|] - {{ - get - {{ - return 123; - }} - set - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public readonly int [|P|] - {{ - get - {{ - return 123; - }} - set - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public struct S + { + public readonly int [|P|] { get; set; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Public Structure S + Public Property [|P|] As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public readonly int [|P|] + { + get + { + return 123; + } + set + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public readonly int [|P|] + { + get + { + return 123; + } + set + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4353,75 +4843,85 @@ public readonly int [|P|] [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructIndexer_ReadOnlyGet(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public readonly int this[int i] => i; -} -"; + var metadataSource = """ + + public struct S + { + public readonly int this[int i] => i; + } + + """; var symbolName = "S.Item"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System.Reflection; - -[DefaultMember(""Item"")] -public struct S -{{ - public readonly int [|this|][int i] {{ get; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System.Reflection - - -Public Structure S - Default Public ReadOnly Property [|Item|](i As Integer) As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public readonly int [|this|][int i] => i; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public readonly int [|this|][int i] => i; -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System.Reflection; + + [DefaultMember("Item")] + public struct S + { + public readonly int [|this|][int i] { get; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System.Reflection + + + Public Structure S + Default Public ReadOnly Property [|Item|](i As Integer) As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public readonly int [|this|][int i] => i; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public readonly int [|this|][int i] => i; + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4431,93 +4931,103 @@ public struct S [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStructIndexer_ReadOnlyGet_Set(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public int this[int i] { readonly get => i; set {} } -} -"; + var metadataSource = """ + + public struct S + { + public int this[int i] { readonly get => i; set {} } + } + + """; var symbolName = "S.Item"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System.Reflection; - -[DefaultMember(""Item"")] -public struct S -{{ - public int [|this|][int i] {{ readonly get; set; }} -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System.Reflection - - -Public Structure S - Default Public Property [|Item|](i As Integer) As Integer -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public int [|this|][int i] - {{ - readonly get - {{ - return i; - }} - set - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public int [|this|][int i] - {{ - readonly get - {{ - return i; - }} - set - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System.Reflection; + + [DefaultMember("Item")] + public struct S + { + public int [|this|][int i] { readonly get; set; } + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System.Reflection + + + Public Structure S + Default Public Property [|Item|](i As Integer) As Integer + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public int [|this|][int i] + { + readonly get + { + return i; + } + set + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public int [|this|][int i] + { + readonly get + { + return i; + } + set + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4527,91 +5037,101 @@ readonly get [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestStruct_ReadOnlyEvent(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public struct S -{ - public readonly event System.Action E { add {} remove {} } -} -"; + var metadataSource = """ + + public struct S + { + public readonly event System.Action E { add {} remove {} } + } + + """; var symbolName = "S.E"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System; - -public struct S -{{ - public readonly event Action [|E|]; -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System - -Public Structure S - Public Event [|E|] As Action -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public readonly event Action [|E|] - {{ - add - {{ - }} - remove - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public struct S -{{ - public readonly event Action [|E|] - {{ - add - {{ - }} - remove - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System; + + public struct S + { + public readonly event Action [|E|]; + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + + Public Structure S + Public Event [|E|] As Action + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public readonly event Action [|E|] + { + add + { + } + remove + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public struct S + { + public readonly event Action [|E|] + { + add + { + } + remove + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4621,92 +5141,102 @@ public readonly event Action [|E|] [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/34650"), CombinatorialData] public async Task TestReadOnlyStruct_ReadOnlyEvent(OriginatingProjectLanguage language, bool signaturesOnly) { - var metadataSource = @" -public readonly struct S -{ - public event System.Action E { add {} remove {} } -} -"; + var metadataSource = """ + + public readonly struct S + { + public event System.Action E { add {} remove {} } + } + + """; var symbolName = "S.E"; var expected = (language, signaturesOnly) switch { - (OriginatingProjectLanguage.CSharp, true) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -using System; - -public readonly struct S -{{ - public event Action [|E|]; -}}", - (OriginatingProjectLanguage.VisualBasic, true) => $@"#Region ""{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"" -' {CodeAnalysisResources.InMemoryAssembly} -#End Region - -Imports System - - -Public Structure S - Public Event [|E|] As Action -End Structure", - (OriginatingProjectLanguage.CSharp, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public readonly struct S -{{ - public event Action [|E|] - {{ - add - {{ - }} - remove - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 6)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", - (OriginatingProjectLanguage.VisualBasic, false) => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {FeaturesResources.location_unknown} -// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} -#endregion - -using System; -using System.Runtime.InteropServices; - -[StructLayout(LayoutKind.Sequential, Size = 1)] -public readonly struct S -{{ - public event Action [|E|] - {{ - add - {{ - }} - remove - {{ - }} - }} -}} -#if false // {FeaturesResources.Decompilation_log} -{string.Format(FeaturesResources._0_items_in_cache, 9)} ------------------- -{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} -{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} -#endif", + (OriginatingProjectLanguage.CSharp, true) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + using System; + + public readonly struct S + { + public event Action [|E|]; + } + """, + (OriginatingProjectLanguage.VisualBasic, true) => $""" + #Region "{FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + ' {CodeAnalysisResources.InMemoryAssembly} + #End Region + + Imports System + + + Public Structure S + Public Event [|E|] As Action + End Structure + """, + (OriginatingProjectLanguage.CSharp, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public readonly struct S + { + public event Action [|E|] + { + add + { + } + remove + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 6)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, + (OriginatingProjectLanguage.VisualBasic, false) => $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{FeaturesResources.location_unknown}} + // Decompiled with ICSharpCode.Decompiler {{ICSharpCodeDecompilerVersion}} + #endregion + + using System; + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential, Size = 1)] + public readonly struct S + { + public event Action [|E|] + { + add + { + } + remove + { + } + } + } + #if false // {{FeaturesResources.Decompilation_log}} + {{string.Format(FeaturesResources._0_items_in_cache, 9)}} + ------------------ + {{string.Format(FeaturesResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")}} + {{string.Format(FeaturesResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")}} + #endif + """, _ => throw ExceptionUtilities.Unreachable(), }; @@ -4716,26 +5246,32 @@ public event Action [|E|] [WpfFact] public async Task TestNotNullCSharpConstraint_Type() { - var metadata = @"using System; -public class TestType where T : notnull -{ -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new [|TestType|]<int>(); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class [|TestType|] where T : notnull -{{ - public TestType(); -}}"; + var metadata = """ + using System; + public class TestType where T : notnull + { + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new [|TestType|]<int>(); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class [|TestType|] where T : notnull + { + public TestType(); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -4753,31 +5289,37 @@ public class [|TestType|] where T : notnull [WpfFact] public async Task TestNotNullCSharpConstraint_Method() { - var metadata = @"using System; -public class TestType -{ - public void M() where T : notnull - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M|]<int>(); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class TestType -{{ - public TestType(); - - public void [|M|]() where T : notnull; -}}"; + var metadata = """ + using System; + public class TestType + { + public void M() where T : notnull + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M|]<int>(); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class TestType + { + public TestType(); + + public void [|M|]() where T : notnull; + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -4795,20 +5337,26 @@ public class TestType [WpfFact] public async Task TestNotNullCSharpConstraint_Delegate() { - var metadata = @"using System; -public delegate void D() where T : notnull;"; - var sourceWithSymbolReference = @" -class C -{ - void M([|D|]<int> lambda) - { - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public delegate void [|D|]() where T : notnull;"; + var metadata = """ + using System; + public delegate void D() where T : notnull; + """; + var sourceWithSymbolReference = """ + + class C + { + void M([|D|]<int> lambda) + { + } + } + """; + var expected = $""" + #region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {CodeAnalysisResources.InMemoryAssembly} + #endregion + + public delegate void [|D|]() where T : notnull; + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -4826,47 +5374,53 @@ class C [WpfFact] public async Task TestNullableEnableDisable1() { - var metadata = @" -#nullable enable + var metadata = """ -using System; + #nullable enable -public class TestType -{ - public void M1(string s) - { - } + using System; -#nullable disable + public class TestType + { + public void M1(string s) + { + } - public void M2(string s) - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](null); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + #nullable disable + + public void M2(string s) + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|](null); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion -#nullable enable + #nullable enable -public class TestType -{{ - public TestType(); + public class TestType + { + public TestType(); - public void [|M1|](string s); -#nullable disable - public void M2(string s); + public void [|M1|](string s); + #nullable disable + public void M2(string s); -#nullable enable -}}"; + #nullable enable + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -4884,44 +5438,50 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable2() { - var metadata = @" -using System; - -public class TestType -{ - public void M1(string s) - { - } - -#nullable enable - - public void M2(string s) - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](null); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -#nullable enable - -public class TestType -{{ - public TestType(); - -#nullable disable - public void [|M1|](string s); -#nullable enable - public void M2(string s); -}}"; + var metadata = """ + + using System; + + public class TestType + { + public void M1(string s) + { + } + + #nullable enable + + public void M2(string s) + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|](null); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + #nullable enable + + public class TestType + { + public TestType(); + + #nullable disable + public void [|M1|](string s); + #nullable enable + public void M2(string s); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -4939,52 +5499,58 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable3() { - var metadata = @" -#nullable enable + var metadata = """ -using System; + #nullable enable -public class TestType -{ - public void M1(string s) - { - } + using System; -#nullable disable + public class TestType + { + public void M1(string s) + { + } - public void M2(string s) - { - } + #nullable disable - public void M3(string s) - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](null); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + public void M2(string s) + { + } + + public void M3(string s) + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|](null); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion -#nullable enable + #nullable enable -public class TestType -{{ - public TestType(); + public class TestType + { + public TestType(); - public void [|M1|](string s); -#nullable disable - public void M2(string s); - public void M3(string s); + public void [|M1|](string s); + #nullable disable + public void M2(string s); + public void M3(string s); -#nullable enable -}}"; + #nullable enable + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5002,39 +5568,45 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable4() { - var metadata = @" -#nullable enable + var metadata = """ -using System; + #nullable enable -public class TestType -{ - public void M1(ICloneable s) - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](null); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + using System; -#nullable enable + public class TestType + { + public void M1(ICloneable s) + { + } + } + """; + var sourceWithSymbolReference = """ -using System; + class C + { + void M() + { + var obj = new TestType().[|M1|](null); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + #nullable enable -public class TestType -{{ - public TestType(); + using System; - public void [|M1|](ICloneable s); -}}"; + public class TestType + { + public TestType(); + + public void [|M1|](ICloneable s); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5052,40 +5624,46 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable5() { - var metadata = @" -#nullable enable + var metadata = """ -using System; + #nullable enable -public class TestType -{ - public void M1(ICloneable s) - { -#nullable disable - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](null); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + using System; -#nullable enable + public class TestType + { + public void M1(ICloneable s) + { + #nullable disable + } + } + """; + var sourceWithSymbolReference = """ -using System; + class C + { + void M() + { + var obj = new TestType().[|M1|](null); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion -public class TestType -{{ - public TestType(); + #nullable enable - public void [|M1|](ICloneable s); -}}"; + using System; + + public class TestType + { + public TestType(); + + public void [|M1|](ICloneable s); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5103,37 +5681,43 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable6() { - var metadata = @" -#nullable enable + var metadata = """ -using System; + #nullable enable -public class TestType -{ - public void M1(T? s) where T : class - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](""""); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + using System; + + public class TestType + { + public void M1(T? s) where T : class + { + } + } + """; + var sourceWithSymbolReference = """ -#nullable enable + class C + { + void M() + { + var obj = new TestType().[|M1|](""); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion -public class TestType -{{ - public TestType(); + #nullable enable - public void [|M1|](T? s) where T : class; -}}"; + public class TestType + { + public TestType(); + + public void [|M1|](T? s) where T : class; + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5151,37 +5735,43 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable7() { - var metadata = @" -#nullable enable + var metadata = """ -using System; + #nullable enable -public class TestType -{ - public void M1(T s) where T : class - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](""""); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + using System; + + public class TestType + { + public void M1(T s) where T : class + { + } + } + """; + var sourceWithSymbolReference = """ -#nullable enable + class C + { + void M() + { + var obj = new TestType().[|M1|](""); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion -public class TestType -{{ - public TestType(); + #nullable enable - public void [|M1|](T s) where T : class; -}}"; + public class TestType + { + public TestType(); + + public void [|M1|](T s) where T : class; + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5199,35 +5789,41 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable8() { - var metadata = @" -#nullable enable - -using System; - -public class TestType -{ - public void M1(T? s) where T : struct - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|]((int?)0); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class TestType -{{ - public TestType(); - - public void [|M1|](T? s) where T : struct; -}}"; + var metadata = """ + + #nullable enable + + using System; + + public class TestType + { + public void M1(T? s) where T : struct + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|]((int?)0); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class TestType + { + public TestType(); + + public void [|M1|](T? s) where T : struct; + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5245,35 +5841,41 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable9() { - var metadata = @" -#nullable enable - -using System; - -public class TestType -{ - public void M1(T s) where T : struct - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](0); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class TestType -{{ - public TestType(); - - public void [|M1|](T s) where T : struct; -}}"; + var metadata = """ + + #nullable enable + + using System; + + public class TestType + { + public void M1(T s) where T : struct + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|](0); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class TestType + { + public TestType(); + + public void [|M1|](T s) where T : struct; + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5291,35 +5893,41 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable10() { - var metadata = @" -#nullable enable - -using System; - -public class TestType -{ - public void M1(T s) - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](""""); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class TestType -{{ - public TestType(); - - public void [|M1|](T s); -}}"; + var metadata = """ + + #nullable enable + + using System; + + public class TestType + { + public void M1(T s) + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|](""); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class TestType + { + public TestType(); + + public void [|M1|](T s); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5337,33 +5945,39 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable11() { - var metadata = @" -using System; - -public class TestType -{ - public void M1(T s) - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](""""); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class TestType -{{ - public TestType(); - - public void [|M1|](T s); -}}"; + var metadata = """ + + using System; + + public class TestType + { + public void M1(T s) + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|](""); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class TestType + { + public TestType(); + + public void [|M1|](T s); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5381,53 +5995,59 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable12() { - var metadata = @" -#nullable enable - -using System; - -namespace N -{ - public class TestType - { - public void M1(string s) - { - } - - #nullable disable - - public void M2(string s) - { - } - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new N.TestType().[|M1|](null); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -#nullable enable - -namespace N -{{ - public class TestType - {{ - public TestType(); - - public void [|M1|](string s); -#nullable disable - public void M2(string s); - -#nullable enable - }} -}}"; + var metadata = """ + + #nullable enable + + using System; + + namespace N + { + public class TestType + { + public void M1(string s) + { + } + + #nullable disable + + public void M2(string s) + { + } + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new N.TestType().[|M1|](null); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + #nullable enable + + namespace N + { + public class TestType + { + public TestType(); + + public void [|M1|](string s); + #nullable disable + public void M2(string s); + + #nullable enable + } + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5445,63 +6065,69 @@ public class TestType [WpfFact] public async Task TestNullableEnableDisable13() { - var metadata = @" -#nullable enable + var metadata = """ -using System; + #nullable enable -public class TestType -{ - public void M1(string s) - { - } + using System; -#nullable disable + public class TestType + { + public void M1(string s) + { + } - public class Nested - { - public void NestedM(string s) - { - } - } + #nullable disable -#nullable enable + public class Nested + { + public void NestedM(string s) + { + } + } - public void M2(string s) - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](null); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion + #nullable enable + + public void M2(string s) + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|](null); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion -#nullable enable + #nullable enable -public class TestType -{{ - public TestType(); + public class TestType + { + public TestType(); - public void [|M1|](string s); - public void M2(string s); + public void [|M1|](string s); + public void M2(string s); - public class Nested - {{ - public Nested(); + public class Nested + { + public Nested(); -#nullable disable - public void NestedM(string s); + #nullable disable + public void NestedM(string s); -#nullable enable - }} -}}"; + #nullable enable + } + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5519,33 +6145,39 @@ public class Nested [WpfFact] public async Task TestDynamic1() { - var metadata = @" -using System; - -public class TestType -{ - public void M1(dynamic s) - { - } -}"; - var sourceWithSymbolReference = @" -class C -{ - void M() - { - var obj = new TestType().[|M1|](null); - } -}"; - var expected = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public class TestType -{{ - public TestType(); - - public void [|M1|](dynamic s); -}}"; + var metadata = """ + + using System; + + public class TestType + { + public void M1(dynamic s) + { + } + } + """; + var sourceWithSymbolReference = """ + + class C + { + void M() + { + var obj = new TestType().[|M1|](null); + } + } + """; + var expected = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public class TestType + { + public TestType(); + + public void [|M1|](dynamic s); + } + """; using var context = TestContext.Create( LanguageNames.CSharp, @@ -5563,27 +6195,31 @@ public class TestType [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/22431")] public async Task TestCDATAComment() { - var source = @" -public enum BinaryOperatorKind -{ - /// - /// Represents the operator. - /// - LeftShift = 0x8, -} -"; + var source = """ + + public enum BinaryOperatorKind + { + /// + /// Represents the operator. + /// + LeftShift = 0x8, + } + + """; var symbolName = "BinaryOperatorKind.LeftShift"; - var expectedCS = $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null -// {CodeAnalysisResources.InMemoryAssembly} -#endregion - -public enum BinaryOperatorKind -{{ - // - // {FeaturesResources.Summary_colon} - // Represents the '<<' operator. - [|LeftShift|] = 8 -}}"; + var expectedCS = $$""" + #region {{FeaturesResources.Assembly}} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + // {{CodeAnalysisResources.InMemoryAssembly}} + #endregion + + public enum BinaryOperatorKind + { + // + // {{FeaturesResources.Summary_colon}} + // Represents the '<<' operator. + [|LeftShift|] = 8 + } + """; await GenerateAndVerifySourceAsync(source, symbolName, LanguageNames.CSharp, expectedCS, includeXmlDocComments: true); } } diff --git a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs index 2e50137e128d2..82fb7dd786c14 100644 --- a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs +++ b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs @@ -18,15 +18,16 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.RenameTracking; [UseExportProvider] [Trait(Traits.Feature, Traits.Features.RenameTracking)] -public class RenameTrackingTaggerProviderTests +public sealed class RenameTrackingTaggerProviderTests { [WpfFact] public async Task RenameTrackingNotOnCreation() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); await state.AssertNoTag(); } @@ -43,10 +44,11 @@ public async Task RenameTrackingNotInBlankFile() [WpfFact] public async Task RenameTrackingTypingAtEnd() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("at"); await state.AssertTag("C", "Cat"); @@ -55,10 +57,11 @@ class C$$ [WpfFact] public async Task RenameTrackingTypingAtBeginning() { - var code = @" -class $$C -{ -}"; + var code = """ + class $$C + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("AB"); await state.AssertTag("C", "ABC"); @@ -67,10 +70,11 @@ class $$C [WpfFact] public async Task RenameTrackingTypingInMiddle() { - var code = @" -class AB$$CD -{ -}"; + var code = """ + class AB$$CD + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("ZZ"); await state.AssertTag("ABCD", "ABZZCD"); @@ -79,10 +83,11 @@ class AB$$CD [WpfFact] public async Task RenameTrackingDeleteFromEnd() { - var code = @" -class ABC$$ -{ -}"; + var code = """ + class ABC$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); await state.AssertTag("ABC", "AB"); @@ -91,10 +96,11 @@ class ABC$$ [WpfFact] public async Task RenameTrackingDeleteFromBeginning() { - var code = @" -class $$ABC -{ -}"; + var code = """ + class $$ABC + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Delete(); await state.AssertTag("ABC", "BC"); @@ -103,10 +109,11 @@ class $$ABC [WpfFact] public async Task RenameTrackingDeleteFromMiddle() { - var code = @" -class AB$$C -{ -}"; + var code = """ + class AB$$C + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); await state.AssertTag("ABC", "AC"); @@ -115,10 +122,11 @@ class AB$$C [WpfFact] public async Task RenameTrackingNotOnClassKeyword() { - var code = @" -class$$ ABCD -{ -}"; + var code = """ + class$$ ABCD + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("d"); await state.AssertNoTag(); @@ -127,15 +135,16 @@ public async Task RenameTrackingNotOnClassKeyword() [WpfFact] public async Task RenameTrackingNotAtMethodArgument() { - var code = @" -class ABCD -{ - void Goo(int x) - { - int abc = 3; - Goo($$ - } -}"; + var code = """ + class ABCD + { + void Goo(int x) + { + int abc = 3; + Goo($$ + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("a"); await state.AssertNoTag(); @@ -147,10 +156,11 @@ void Goo(int x) [WpfFact] public async Task RenameTrackingSessionContinuesAfterViewingTag() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("at"); await state.AssertTag("C", "Cat"); @@ -162,14 +172,15 @@ class C$$ [WpfFact] public async Task RenameTrackingNotInString() { - var code = @" -class C -{ - void Goo() - { - string s = ""abc$$"" - } -}"; + var code = """ + class C + { + void Goo() + { + string s = "abc$$" + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("d"); await state.AssertNoTag(); @@ -178,10 +189,11 @@ void Goo() [WpfFact] public async Task RenameTrackingHandlesAtSignAsCSharpEscape() { - var code = @" -class $$C -{ -}"; + var code = """ + class $$C + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("@"); await state.AssertTag("C", "@C"); @@ -190,9 +202,10 @@ class $$C [WpfFact] public async Task RenameTrackingHandlesSquareBracketsAsVisualBasicEscape() { - var code = @" -Class $$C -End Class"; + var code = """ + Class $$C + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.InsertText("["); await state.AssertNoTag(); @@ -205,10 +218,11 @@ public async Task RenameTrackingHandlesSquareBracketsAsVisualBasicEscape() [WpfFact] public async Task RenameTrackingNotOnSquareBracketsInCSharp() { - var code = @" -class $$C -{ -}"; + var code = """ + class $$C + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("["); await state.AssertNoTag(); @@ -221,10 +235,11 @@ class $$C [WpfFact] public async Task RenameTrackingHandlesUnicode() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("\u0414\u046E\u046A\u00DB\u00CA\u00DB\u00C4\u00C1\u00CD\u00E4\u00E1\u0152\u0178\u00F5\u00E0\u0178\u00FC\u00C4\u00B5\u00C1i\u00DBE\u00EA\u00E0\u00EA\u00E8\u00E4\u00E5\u00ED\u00F2\u00E8\u00F4\u00E8\u00EA\u00E0\u00F2\u00EE\u00F0\u00F1\u00EB\u00EE\u00E2\u00EE"); await state.AssertTag("C", "C\u0414\u046E\u046A\u00DB\u00CA\u00DB\u00C4\u00C1\u00CD\u00E4\u00E1\u0152\u0178\u00F5\u00E0\u0178\u00FC\u00C4\u00B5\u00C1i\u00DBE\u00EA\u00E0\u00EA\u00E8\u00E4\u00E5\u00ED\u00F2\u00E8\u00F4\u00E8\u00EA\u00E0\u00F2\u00EE\u00F0\u00F1\u00EB\u00EE\u00E2\u00EE"); @@ -233,10 +248,11 @@ class C$$ [WpfFact] public async Task RenameTrackingThroughKeyword() { - var code = @" -class i$$ -{ -}"; + var code = """ + class i$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("n"); await state.AssertNoTag(); @@ -251,10 +267,11 @@ class i$$ [WpfFact] public async Task RenameTrackingThroughIllegalStartCharacter() { - var code = @" -class $$abc -{ -}"; + var code = """ + class $$abc + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("9"); await state.AssertNoTag(); @@ -267,10 +284,11 @@ class $$abc [WpfFact] public async Task RenameTrackingOnBothSidesOfIdentifier() { - var code = @" -class $$Def -{ -}"; + var code = """ + class $$Def + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("Abc"); await state.AssertTag("Def", "AbcDef"); @@ -283,10 +301,11 @@ class $$Def [WpfFact] public async Task RenameTrackingThroughSameIdentifier() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("s"); await state.AssertTag("C", "Cs"); @@ -301,10 +320,11 @@ class C$$ [WpfFact] public async Task RenameTrackingThroughEmptyString() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); await state.AssertNoTag(); @@ -316,10 +336,11 @@ class C$$ [WpfFact] public async Task RenameTrackingThroughEmptyStringWithCaretMove() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); state.MoveCaret(-4); @@ -333,10 +354,11 @@ class C$$ [WpfFact] public async Task RenameTrackingNotThroughEmptyStringResumeOnDifferentSpace() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); @@ -350,10 +372,11 @@ class C$$ [WpfFact] public async Task RenameTrackingReplaceIdentifierSuffix() { - var code = @" -class Identifi[|er|]$$ -{ -}"; + var code = """ + class Identifi[|er|]$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); var textSpan = state.HostDocument.SelectedSpans.Single(); state.EditorOperations.ReplaceText(new Span(textSpan.Start, textSpan.Length), "cation"); @@ -363,10 +386,11 @@ class Identifi[|er|]$$ [WpfFact] public async Task RenameTrackingReplaceIdentifierPrefix() { - var code = @" -class $$[|Ident|]ifier -{ -}"; + var code = """ + class $$[|Ident|]ifier + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); var textSpan = state.HostDocument.SelectedSpans.Single(); state.EditorOperations.ReplaceText(new Span(textSpan.Start, textSpan.Length), "Complex"); @@ -376,10 +400,11 @@ class $$[|Ident|]ifier [WpfFact] public async Task RenameTrackingReplaceIdentifierCompletely() { - var code = @" -class [|Cat|]$$ -{ -}"; + var code = """ + class [|Cat|]$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); var textSpan = state.HostDocument.SelectedSpans.Single(); state.EditorOperations.ReplaceText(new Span(textSpan.Start, textSpan.Length), "Dog"); @@ -389,16 +414,17 @@ class [|Cat|]$$ [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/34280")] public async Task RenameTrackingReplaceIdentifierWithDiscard() { - var code = @" -class Class -{ - int Method() - { - int i; - [|i|]$$ = Method(); - rteurn 0; - } -}"; + var code = """ + class Class + { + int Method() + { + int i; + [|i|]$$ = Method(); + rteurn 0; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); var textSpan = state.HostDocument.SelectedSpans.Single(); state.EditorOperations.ReplaceText(new Span(textSpan.Start, textSpan.Length), "_"); @@ -408,10 +434,11 @@ int Method() [WpfFact] public async Task RenameTrackingNotAfterInvoke() { - var code = @" -class Cat$$ -{ -}"; + var code = """ + class Cat$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("s"); await state.AssertTag("Cat", "Cats", invokeAction: true); @@ -422,10 +449,11 @@ class Cat$$ [WpfFact] public async Task RenameTrackingInvokeAndChangeBackToOriginal() { - var code = @" -class Cat$$ -{ -}"; + var code = """ + class Cat$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("s"); await state.AssertTag("Cat", "Cats", invokeAction: true); @@ -439,10 +467,11 @@ class Cat$$ [WpfFact] public async Task RenameTrackingUndoOnceAndStartNewSession() { - var code = @" -class Cat$$ -{ -}"; + var code = """ + class Cat$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("abc"); await state.AssertTag("Cat", "Catabc", invokeAction: true); @@ -460,10 +489,11 @@ class Cat$$ [WpfFact] public async Task RenameTrackingUndoTwiceAndContinueSession() { - var code = @" -class Cat$$ -{ -}"; + var code = """ + class Cat$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("abc"); await state.AssertTag("Cat", "Catabc", invokeAction: true); @@ -481,10 +511,11 @@ class Cat$$ [WpfFact] public async Task RenameTrackingRedoAlwaysClearsState() { - var code = @" -class Cat$$ -{ -}"; + var code = """ + class Cat$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("s"); await state.AssertTag("Cat", "Cats", invokeAction: true); @@ -505,10 +536,11 @@ class Cat$$ [WpfFact] public async Task RenameTrackingUndoTwiceRedoTwiceUndoStillWorks() { - var code = @" -class Cat$$ -{ -}"; + var code = """ + class Cat$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("s"); await state.AssertTag("Cat", "Cats", invokeAction: true); @@ -534,14 +566,15 @@ class Cat$$ [WpfFact] public async Task RenameTrackingOnReference_ParameterAsArgument() { - var code = @" -class C -{ - void M(int x) - { - M(x$$); - } -}"; + var code = """ + class C + { + void M(int x) + { + M(x$$); + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("yz"); await state.AssertTag("x", "xyz"); @@ -550,14 +583,15 @@ void M(int x) [WpfFact] public async Task RenameTrackingOnReference_ParameterAsNamedArgument() { - var code = @" -class C -{ - void M(int x) - { - M(x$$: x); - } -}"; + var code = """ + class C + { + void M(int x) + { + M(x$$: x); + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("yz"); await state.AssertTag("x", "xyz"); @@ -566,17 +600,18 @@ void M(int x) [WpfFact] public async Task RenameTrackingOnReference_Namespace() { - var code = @" -namespace NS -{ - class C - { - static void M() - { - NS$$.C.M(); - } - } -}"; + var code = """ + namespace NS + { + class C + { + static void M() + { + NS$$.C.M(); + } + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("A"); await state.AssertTag("NS", "NSA"); @@ -585,118 +620,120 @@ static void M() [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/21657")] public async Task RenameTrackingOnReference_Attribute_CSharp() { - var code = @" -using System; + var code = """ + using System; -class [|$$ustom|]Attribute : Attribute -{ -} -"; + class [|$$ustom|]Attribute : Attribute + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("C"); await state.AssertTag("ustomAttribute", "CustomAttribute", invokeAction: true); - var expectedCode = @" -using System; + var expectedCode = """ + using System; -class CustomAttribute : Attribute -{ -} -"; + class CustomAttribute : Attribute + { + } + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/21657")] public async Task RenameTrackingOnReference_Attribute_VB() { - var code = @" -Import System; + var code = """ + Import System; -Public Class [|$$ustom|]Attribute - Inherits Attribute -End Class -"; + Public Class [|$$ustom|]Attribute + Inherits Attribute + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.InsertText("C"); await state.AssertTag("ustomAttribute", "CustomAttribute", invokeAction: true); - var expectedCode = @" -Import System; + var expectedCode = """ + Import System; -Public Class CustomAttribute - Inherits Attribute -End Class -"; + Public Class CustomAttribute + Inherits Attribute + End Class + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/21657")] public async Task RenameTrackingOnReference_Capitalized_Attribute_VB() { - var code = @" -Import System; + var code = """ + Import System; -Public Class [|$$ustom|]ATTRIBUTE - Inherits Attribute -End Class -"; + Public Class [|$$ustom|]ATTRIBUTE + Inherits Attribute + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.InsertText("C"); await state.AssertTag("ustomATTRIBUTE", "CustomATTRIBUTE", invokeAction: true); - var expectedCode = @" -Import System; + var expectedCode = """ + Import System; -Public Class CustomATTRIBUTE - Inherits Attribute -End Class -"; + Public Class CustomATTRIBUTE + Inherits Attribute + End Class + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/21657")] public async Task RenameTrackingOnReference_Not_Capitalized_Attribute_VB() { - var code = @" -Import System; + var code = """ + Import System; -Public Class [|$$ustom|]attribute - Inherits Attribute -End Class -"; + Public Class [|$$ustom|]attribute + Inherits Attribute + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.InsertText("C"); await state.AssertTag("ustomattribute", "Customattribute", invokeAction: true); - var expectedCode = @" -Import System; + var expectedCode = """ + Import System; -Public Class Customattribute - Inherits Attribute -End Class -"; + Public Class Customattribute + Inherits Attribute + End Class + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); } [WpfFact] public async Task RenameTrackingNotifiesThirdPartiesOfRenameOperation() { - var code = @" -class Cat$$ -{ - public Cat() - { - } -}"; + var code = """ + class Cat$$ + { + public Cat() + { + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("s"); await state.AssertTag("Cat", "Cats", invokeAction: true); Assert.Equal(1, state.RefactorNotifyService.OnBeforeSymbolRenamedCount); Assert.Equal(1, state.RefactorNotifyService.OnAfterSymbolRenamedCount); - var expectedCode = @" -class Cats -{ - public Cats() - { - } -}"; + var expectedCode = """ + class Cats + { + public Cats() + { + } + } + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); state.AssertNoNotificationMessage(); @@ -706,13 +743,14 @@ public Cats() [WpfFact] public async Task RenameTrackingHonorsThirdPartyRequestsForCancellationBeforeRename() { - var code = @" -class Cat$$ -{ - public Cat() - { - } -}"; + var code = """ + class Cat$$ + { + public Cat() + { + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp, onBeforeGlobalSymbolRenamedReturnValue: false); state.EditorOperations.InsertText("s"); await state.AssertTag("Cat", "Cats", invokeAction: true); @@ -722,13 +760,14 @@ public Cat() Assert.Equal(0, state.RefactorNotifyService.OnAfterSymbolRenamedCount); await state.AssertNoTag(); - var expectedCode = @" -class Cat -{ - public Cat() - { - } -}"; + var expectedCode = """ + class Cat + { + public Cat() + { + } + } + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); state.AssertNotificationMessage(); @@ -737,13 +776,14 @@ public Cat() [WpfFact] public async Task RenameTrackingAlertsAboutThirdPartyRequestsForCancellationAfterRename() { - var code = @" -class Cat$$ -{ - public Cat() - { - } -}"; + var code = """ + class Cat$$ + { + public Cat() + { + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp, onAfterGlobalSymbolRenamedReturnValue: false); state.EditorOperations.InsertText("s"); await state.AssertTag("Cat", "Cats", invokeAction: true); @@ -753,13 +793,14 @@ public Cat() state.AssertNotificationMessage(); // Make sure the rename completed - var expectedCode = @" -class Cats -{ - public Cats() - { - } -}"; + var expectedCode = """ + class Cats + { + public Cats() + { + } + } + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); await state.AssertNoTag(); } @@ -767,12 +808,13 @@ public Cats() [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530469")] public async Task RenameTrackingNotWhenStartedFromTextualWordInTrivia() { - var code = @" -Module Program - Sub Main() - Dim [x$$ = 1 - End Sub -End Module"; + var code = """ + Module Program + Sub Main() + Dim [x$$ = 1 + End Sub + End Module + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.InsertText("]"); await state.AssertNoTag(); @@ -781,12 +823,13 @@ End Sub [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530495")] public async Task RenameTrackingNotWhenCaseCorrectingReference() { - var code = @" -Module Program - Sub Main() - $$main() - End Sub -End Module"; + var code = """ + Module Program + Sub Main() + $$main() + End Sub + End Module + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.Delete(); await state.AssertTag("main", "ain"); @@ -797,14 +840,15 @@ End Sub [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/599508")] public async Task RenameTrackingNotWhenNewIdentifierReferenceBinds() { - var code = @" -Module Program - Sub Main() - $$[|main|]() - End Sub - Sub Goo() - End Sub -End Module"; + var code = """ + Module Program + Sub Main() + $$[|main|]() + End Sub + Sub Goo() + End Sub + End Module + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); var textSpan = state.HostDocument.SelectedSpans.Single(); state.EditorOperations.ReplaceText(new Span(textSpan.Start, textSpan.Length), "Go"); @@ -816,10 +860,11 @@ End Sub [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530400")] public async Task RenameTrackingNotWhenDeclaringEnumMembers() { - var code = @" -Enum E -$$ -End Enum"; + var code = """ + Enum E + $$ + End Enum + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.InsertText(" a"); state.EditorOperations.InsertText("b"); @@ -857,17 +902,44 @@ public void RenameTrackingDoesNotThrowAggregateException() Assert.Same(thrownException, caughtException); } - [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1063943")] - public async Task RenameTrackingNotFromReferenceWithWrongNumberOfArguments() - { - var code = @" -class C -{ - void M(int x) - { - M$$(); + [WpfFact] + [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1063943")] + [WorkItem("https://github.com/dotnet/roslyn/issues/10914")] + public async Task RenameTrackingOnReferenceWithWrongNumberOfArguments() + { + var code = """ + class C + { + void M(int x) + { + M$$(); + } + } + """; + + using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); + state.EditorOperations.InsertText("eow"); + await state.AssertTag("M", "Meow"); } -}"; + + [WpfFact] + [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1063943")] + [WorkItem("https://github.com/dotnet/roslyn/issues/10914")] + public async Task RenameTrackingOnReferenceWithWrongNumberOfArguments_Overloads() + { + var code = """ + class C + { + void M(int x) + { + M$$(); + } + + void M(bool x) + { + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("eow"); @@ -877,10 +949,11 @@ void M(int x) [WpfFact] public async Task CancelRenameTracking() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("at"); await state.AssertTag("C", "Cat"); @@ -891,10 +964,11 @@ class C$$ [WpfFact] public async Task RenameTrackingNotWhenDeclaringEnumMembersEvenAfterCancellation() { - var code = @" -Enum E -$$ -End Enum"; + var code = """ + Enum E + $$ + End Enum + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.InsertText(" a"); state.EditorOperations.InsertText("b"); @@ -907,10 +981,11 @@ Enum E [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/540")] public async Task RenameTrackingDoesNotProvideDiagnosticAfterCancellation() { - var code = @" -class C$$ -{ -}"; + var code = """ + class C$$ + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("at"); await state.AssertTag("C", "Cat"); @@ -926,36 +1001,38 @@ class C$$ [WpfFact] public async Task RenameTracking_Nameof_FromMethodGroupReference() { - var code = @" -class C -{ - void M() - { - nameof(M$$).ToString(); - } - - void M(int x) - { - } -}"; + var code = """ + class C + { + void M() + { + nameof(M$$).ToString(); + } + + void M(int x) + { + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("at"); await state.AssertTag("M", "Mat", invokeAction: true); // Make sure the rename completed - var expectedCode = @" -class C -{ - void Mat() - { - nameof(Mat).ToString(); - } - - void Mat(int x) - { - } -}"; + var expectedCode = """ + class C + { + void Mat() + { + nameof(Mat).ToString(); + } + + void Mat(int x) + { + } + } + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); await state.AssertNoTag(); } @@ -963,28 +1040,30 @@ void Mat(int x) [WpfFact] public async Task RenameTracking_Nameof_FromMethodDefinition_NoOverloads() { - var code = @" -class C -{ - void M$$() - { - nameof(M).ToString(); - } -}"; + var code = """ + class C + { + void M$$() + { + nameof(M).ToString(); + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("at"); await state.AssertTag("M", "Mat", invokeAction: true); // Make sure the rename completed - var expectedCode = @" -class C -{ - void Mat() - { - nameof(Mat).ToString(); - } -}"; + var expectedCode = """ + class C + { + void Mat() + { + nameof(Mat).ToString(); + } + } + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); await state.AssertNoTag(); } @@ -992,36 +1071,38 @@ void Mat() [WpfFact] public async Task RenameTracking_Nameof_FromMethodDefinition_WithOverloads() { - var code = @" -class C -{ - void M$$() - { - nameof(M).ToString(); - } - - void M(int x) - { - } -}"; + var code = """ + class C + { + void M$$() + { + nameof(M).ToString(); + } + + void M(int x) + { + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("at"); await state.AssertTag("M", "Mat", invokeAction: true); // Make sure the rename completed - var expectedCode = @" -class C -{ - void Mat() - { - nameof(M).ToString(); - } - - void M(int x) - { - } -}"; + var expectedCode = """ + class C + { + void Mat() + { + nameof(M).ToString(); + } + + void M(int x) + { + } + } + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); await state.AssertNoTag(); } @@ -1029,14 +1110,15 @@ void M(int x) [WpfFact] public async Task RenameTracking_Nameof_FromReferenceToMetadata_NoTag() { - var code = @" -class C -{ - void M() - { - var x = nameof(ToString$$); - } -}"; + var code = """ + class C + { + void M() + { + var x = nameof(ToString$$); + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("z"); await state.AssertNoTag(); @@ -1045,16 +1127,17 @@ void M() [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/762964")] public async Task RenameTracking_NoTagWhenFirstEditChangesReferenceToAnotherSymbol() { - var code = @" -class C -{ - void M() - { - int abc = 7; - int ab = 8; - int z = abc$$; - } -}"; + var code = """ + class C + { + void M() + { + int abc = 7; + int ab = 8; + int z = abc$$; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); await state.AssertNoTag(); @@ -1063,14 +1146,15 @@ void M() [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/2605")] public async Task RenameTracking_CannotRenameToVarInCSharp() { - var code = @" -class C -{ - void M() - { - C$$ c; - } -}"; + var code = """ + class C + { + void M() + { + C$$ c; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); state.EditorOperations.InsertText("va"); @@ -1090,14 +1174,15 @@ void M() [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/2605")] public async Task RenameTracking_CannotRenameFromVarInCSharp() { - var code = @" -class C -{ - void M() - { - var$$ c = new C(); - } -}"; + var code = """ + class C + { + void M() + { + var$$ c = new C(); + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); await state.AssertNoTag(); @@ -1107,12 +1192,13 @@ void M() [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/2605")] public async Task RenameTracking_CanRenameToVarInVisualBasic() { - var code = @" -Class C - Sub M() - Dim x as C$$ - End Sub -End Class"; + var code = """ + Class C + Sub M() + Dim x as C$$ + End Sub + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.Backspace(); state.EditorOperations.InsertText("var"); @@ -1124,14 +1210,15 @@ End Sub [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/2605")] public async Task RenameTracking_CannotRenameToDynamicInCSharp() { - var code = @" -class C -{ - void M() - { - C$$ c; - } -}"; + var code = """ + class C + { + void M() + { + C$$ c; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); state.EditorOperations.InsertText("dynami"); @@ -1151,15 +1238,16 @@ void M() [WpfFact] public async Task RenameImplicitTupleField() { - var code = @" -class C -{ - void M() - { - (int, int) x = (1, 2); - var y = x.Item1$$; - } -}"; + var code = """ + class C + { + void M() + { + (int, int) x = (1, 2); + var y = x.Item1$$; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); state.EditorOperations.Backspace(); @@ -1170,14 +1258,14 @@ void M() [WpfFact] public async Task RenameImplicitTupleFieldVB() { - var code = @" -class C - Sub M() - Dim x as (Integer, Integer) = (1, 2) - Dim y = x.Item1$$ - End Sub -End Class -"; + var code = """ + class C + Sub M() + Dim x as (Integer, Integer) = (1, 2) + Dim y = x.Item1$$ + End Sub + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.Backspace(); state.EditorOperations.Backspace(); @@ -1188,16 +1276,16 @@ End Class [WpfFact] public async Task RenameImplicitTupleFieldExtended() { - var code = @" -class C -{ - void M() - { - (int, int, int, int, int, int, int, int, int, int) x = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - var y = x.Item9$$; - } -} -"; + var code = """ + class C + { + void M() + { + (int, int, int, int, int, int, int, int, int, int) x = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + var y = x.Item9$$; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); state.EditorOperations.Backspace(); @@ -1208,14 +1296,14 @@ void M() [WpfFact] public async Task RenameImplicitTupleFieldExtendedVB() { - var code = @" -Class C - Sub M() - Dim x as (Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer) = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) - Dim y = x.Item9$$ - End Sub -End Class -"; + var code = """ + Class C + Sub M() + Dim x as (Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer) = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + Dim y = x.Item9$$ + End Sub + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.Backspace(); state.EditorOperations.Backspace(); @@ -1226,15 +1314,16 @@ End Class [WpfFact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems?_a=edit&id=371205")] public async Task RenameTrackingNotOnExplicitTupleReturnDeclaration_CSharp() { - var code = @" -class C -{ - void M() - { - (int abc$$, int) x = (1, 2); - var y = x.abc; - } -}"; + var code = """ + class C + { + void M() + { + (int abc$$, int) x = (1, 2); + var y = x.abc; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); state.EditorOperations.Backspace(); @@ -1245,13 +1334,14 @@ void M() [WpfFact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems?_a=edit&id=371205")] public async Task RenameTrackingNotOnExplicitTupleReturnDeclaration_VB() { - var code = @" -class C - Sub M() - Dim x as (abc$$ as integer, int Item2 as integer) = (1, 2) - Dim y = x.abc - End Sub -End Class"; + var code = """ + class C + Sub M() + Dim x as (abc$$ as integer, int Item2 as integer) = (1, 2) + Dim y = x.abc + End Sub + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.Backspace(); state.EditorOperations.Backspace(); @@ -1262,15 +1352,16 @@ End Sub [WpfFact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems?_a=edit&id=371205")] public async Task RenameTrackingNotOnExplicitTupleFieldReference_CSharp() { - var code = @" -class C -{ - void M() - { - (int abc, int) x = (1, 2); - var y = x.abc$$; - } -}"; + var code = """ + class C + { + void M() + { + (int abc, int) x = (1, 2); + var y = x.abc$$; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.Backspace(); state.EditorOperations.Backspace(); @@ -1281,13 +1372,14 @@ void M() [WpfFact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems?_a=edit&id=371205")] public async Task RenameTrackingNotOnExplicitTupleFieldReference_VB() { - var code = @" -class C - Sub M() - Dim x as (abc as integer, int Item2 as integer) = (1, 2) - Dim y = x.abc$$ - End Sub -End Class"; + var code = """ + class C + Sub M() + Dim x as (abc as integer, int Item2 as integer) = (1, 2) + Dim y = x.abc$$ + End Sub + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.Backspace(); state.EditorOperations.Backspace(); @@ -1298,14 +1390,15 @@ End Sub [WpfFact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems?_a=edit&id=371205")] public async Task RenameTrackingNotOnExplicitTupleElementsInDeclarations_CSharp() { - var code = @" -class C -{ - void M() - { - var t = (x$$: 1, y: 2); - } -}"; + var code = """ + class C + { + void M() + { + var t = (x$$: 1, y: 2); + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("2"); await state.AssertNoTag(); @@ -1314,12 +1407,13 @@ void M() [WpfFact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems?_a=edit&id=371205")] public async Task RenameTrackingNotOnExplicitTupleElementsInDeclarations_VB() { - var code = @" -Class C - Sub M() - Dim t = (x$$:=1, y:=2) - End Sub -End Class"; + var code = """ + Class C + Sub M() + Dim t = (x$$:=1, y:=2) + End Sub + End Class + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.VisualBasic); state.EditorOperations.InsertText("2"); await state.AssertNoTag(); @@ -1328,30 +1422,31 @@ End Sub [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/14159")] public async Task RenameTrackingNotOnWellKnownValueTupleType() { - var workspaceXml = @" - - - -using System; - -class C -{ - void M() - { - var x = new ValueTuple$$<int>(); - } -} - -namespace System -{ - public struct ValueTuple<T1> - { - public T1 Item1; - } -} - - -"; + var workspaceXml = """ + + + + using System; + + class C + { + void M() + { + var x = new ValueTuple$$<int>(); + } + } + + namespace System + { + public struct ValueTuple<T1> + { + public T1 Item1; + } + } + + + + """; using var state = RenameTrackingTestState.CreateFromWorkspaceXml(workspaceXml, LanguageNames.CSharp); state.EditorOperations.InsertText("2"); await state.AssertNoTag(); @@ -1360,25 +1455,26 @@ public struct ValueTuple<T1> [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/14159")] public async Task RenameTrackingOnThingsCalledValueTupleThatAreNotTheWellKnownType() { - var workspaceXml = @" - - - -class C -{ - void M() - { - var x = new ValueTuple$$<int>(); - } -} - -public struct ValueTuple<T1> -{ - public T1 Item1; -} - - -"; + var workspaceXml = """ + + + + class C + { + void M() + { + var x = new ValueTuple$$<int>(); + } + } + + public struct ValueTuple<T1> + { + public T1 Item1; + } + + + + """; using var state = RenameTrackingTestState.CreateFromWorkspaceXml(workspaceXml, LanguageNames.CSharp); state.EditorOperations.InsertText("2"); await state.AssertTag("ValueTuple", "ValueTuple2"); @@ -1387,15 +1483,16 @@ public struct ValueTuple<T1> [WpfFact] public async Task RenameTrackingOnDeconstruct() { - var code = @" -class C -{ - void Deconstruct$$(out int x1, out int x2) { x1 = 1; x2 = 2; } - void M() - { - var (y1, y2) = this; - } -}"; + var code = """ + class C + { + void Deconstruct$$(out int x1, out int x2) { x1 = 1; x2 = 2; } + void M() + { + var (y1, y2) = this; + } + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("2"); await state.AssertTag("Deconstruct", "Deconstruct2"); @@ -1404,10 +1501,11 @@ void M() [WpfFact] public async Task RenameTracking_UnmanagedConstraint_Keyword() { - var code = @" -class C<T> where T : $$unmanaged -{ -}"; + var code = """ + class C<T> where T : $$unmanaged + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); await state.AssertNoTag(); } @@ -1415,26 +1513,28 @@ class C<T> where T : $$unmanaged [WpfFact] public async Task RenameTracking_UnmanagedConstraint_Type() { - var code = @" -interface unmanaged -{ -} -class C<T> where T : $$unmanaged -{ -}"; + var code = """ + interface unmanaged + { + } + class C<T> where T : $$unmanaged + { + } + """; using var state = RenameTrackingTestState.Create(code, LanguageNames.CSharp); state.EditorOperations.InsertText("my"); await state.AssertTag("unmanaged", "myunmanaged", invokeAction: true); // Make sure the rename completed - var expectedCode = @" -interface myunmanaged -{ -} -class C where T : myunmanaged -{ -}"; + var expectedCode = """ + interface myunmanaged + { + } + class C where T : myunmanaged + { + } + """; Assert.Equal(expectedCode, state.HostDocument.GetTextBuffer().CurrentSnapshot.GetText()); await state.AssertNoTag(); } diff --git a/src/EditorFeatures/Test/Utilities/PatternMatcherTests.cs b/src/EditorFeatures/Test/Utilities/PatternMatcherTests.cs index 70f63b4c7c1b6..56bd915658eea 100644 --- a/src/EditorFeatures/Test/Utilities/PatternMatcherTests.cs +++ b/src/EditorFeatures/Test/Utilities/PatternMatcherTests.cs @@ -193,8 +193,10 @@ private static void VerifyBreakIntoCharacterParts(string original, params string [InlineData("Combine[|Bin|]ary", "bin", PatternMatchKind.StartOfWordSubstring, CaseInsensitive)] [InlineData("_ABC_[|Abc|]_", "Abc", PatternMatchKind.StartOfWordSubstring, CaseSensitive)] + [InlineData("[|C|]reate[|R|]ange", "CR", PatternMatchKind.CamelCaseExact, CaseSensitive)] [WorkItem("https://github.com/dotnet/roslyn/issues/51029")] + [WorkItem("https://github.com/dotnet/roslyn/issues/17275")] internal void TestNonFuzzyMatch( string candidate, string pattern, PatternMatchKind matchKind, bool isCaseSensitive) { diff --git a/src/EditorFeatures/Test2/CodeDefinitionWindow/CrossLanguageCodeDefinitionWindowTests.vb b/src/EditorFeatures/Test2/CodeDefinitionWindow/CrossLanguageCodeDefinitionWindowTests.vb index 84c3fa5602650..e38537ca2c832 100644 --- a/src/EditorFeatures/Test2/CodeDefinitionWindow/CrossLanguageCodeDefinitionWindowTests.vb +++ b/src/EditorFeatures/Test2/CodeDefinitionWindow/CrossLanguageCodeDefinitionWindowTests.vb @@ -89,6 +89,10 @@ Namespace Microsoft.CodeAnalysis.Editor.CodeDefinitionWindow.UnitTests Public Function GetNavigableItemsAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of INavigableItem)) Implements INavigableItemsService.GetNavigableItemsAsync Return Task.FromResult(ImmutableArray.Create(Of INavigableItem)(New FakeNavigableItem(document))) End Function + + Public Function GetNavigableItemsAsync(document As Document, position As Integer, forSymbolType As Boolean, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of INavigableItem)) Implements INavigableItemsService.GetNavigableItemsAsync + Return Task.FromResult(ImmutableArray.Create(Of INavigableItem)(New FakeNavigableItem(document))) + End Function End Class diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb index 19a9f764f34be..088b930be272f 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb @@ -271,7 +271,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim expectedDiagnostics = GetExpectedDiagnostics(workspace, diagnostics) If ordered Then - AssertEx.Equal(expectedDiagnostics, actualDiagnostics, New Comparer()) + AssertEx.SequenceEqual(expectedDiagnostics, actualDiagnostics, New Comparer()) Else AssertEx.SetEqual(expectedDiagnostics, actualDiagnostics, New Comparer()) End If diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb new file mode 100644 index 0000000000000..60f7e7b246fb0 --- /dev/null +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.PreprocessingSymbols.vb @@ -0,0 +1,339 @@ + +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis.Remote.Testing + +Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences + + Partial Public Class FindReferencesTests + + + Public Async Function TestPreprocessingSymbolBasic(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define [|PREPROCESSING_SYMBOL|] +#define MORE_PREPROCESSING_SYMBOL + +#if [|PREPROCESSING_SYMBOL|] +namespace SimpleNamespace; +#elif true && (!false || [|PRE$$PROCESSING_SYMBOL|]) +namespace AnotherNamespace; +#elif MORE_PREPROCESSING_SYMBOL +namespace MoreSimpleNamespace; +#else +namespace ComplexNamespace; +#endif + +// PREPROCESSING_SYMBOL +class PREPROCESSING_SYMBOL +{ +} + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestNotPreprocessingSymbol(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define PREPROCESSING_SYMBOL +#define MORE_PREPROCESSING_SYMBOL + +#if PREPROCESSING_SYMBOL +namespace SimpleNamespace; +#elif true && (!false || PREPROCESSING_SYMBOL) +namespace AnotherNamespace; +#elif MORE_PREPROCESSING_SYMBOL +namespace MoreSimpleNamespace; +#else +namespace ComplexNamespace; +#endif + +// PREPROCESSING_SYMBOL +class {|Definition:PREPROCES$$SING_SYMBOL|} +{ +} + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolRegion(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define PREPROCESSING_SYMBOL + +class Class +{ + #region PREP$$ROCESSING_SYMBOL + public void Method() { } + #endregion PREPROCESSING_SYMBOL +} + +#if NOT_PREPROCESSING_SYMBOL +#elif PREPROCESSING_SYMBOL +#endif + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolPragmaWarning(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define PREPROCESSING_SYMBOL + +class Class +{ + #pragma warning disable PREPROCESSING_SYMBOL + public void Method() { } + #pragma warning restore PREPROC$$ESSING_SYMBOL +} + +#if NOT_PREPROCESSING_SYMBOL +#elif PREPROCESSING_SYMBOL +#endif + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolMultipleDocuments(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define [|PREPROCESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] +#define [|PREPROCESSING_SYMBOL|] + +class Class +{ + #region PREPROCESSING_SYMBOL + public void Method() { } + #endregion PREPROCESSING_SYMBOL +} + +#if NOT_PREPROCESSING_SYMBOL +#elif [|PREPROC$$ESSING_SYMBOL|] +#endif + + +#if ![|PREPROCESSING_SYMBOL|] +#define [|PREPROCESSING_SYMBOL|] +#elif [|PREPROCESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] +#endif + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolMultipleProjects1(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#if NOT_PREPROCESSING_SYMBOL +#elif [|PREPROCESSING_SYMBO$$L|] +#endif + + +#if ![|PREPROCESSING_SYMBOL|] +#define [|PREPROCESSING_SYMBOL|] +#elif [|PREPROCESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] +#endif + + + + +#define [|PREPROCESSING_SYMBOL|] + +#if [|PREPROCESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] +#endif + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolMultipleProjects2HoverCSharp(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define [|PREPROCESSING_SYMBOL|] + +#if [|PREPROCESSING_SYM$$BOL|] +#undef [|PREPROCESSING_SYMBOL|] +#endif + + + + +#Const [|PREPROCESSING_SYMBOL|] = True + + +#If DEBUG Then +' Some code +#ElseIf NOT_PREPROCESSING_SYMBOL = [|PREPROCESSING_SYMBOL|] Then +#End If + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolMultipleProjects2HoverVB(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define [|PREPROCESSING_SYMBOL|] + +#if [|PREPROCESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] +#endif + + + + +#Const [|PREPROCESSING_SYMBOL|] = True + + +#If DEBUG Then +' Some code +#ElseIf NOT_PREPROCESSING_SYMBOL = [|$$PREPROCESSING_SYMBOL|] Then +#End If + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolUsedInSourceGeneratedDocument(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#if ![|PREPROCESSING_SYMBOL|] +#define [|PREPROCESSING$$_SYMBOL|] +#elif [|PREPROCESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] +#endif + + +#if ![|PREPROCESSING_SYMBOL|] +#define [|PREPROCESSING_SYMBOL|] +#elif [|PREPROCESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] +#endif + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolHoverDefine(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define [|PREPROC$$ESSING_SYMBOL|] +#undef [|PREPROCESSING_SYMBOL|] + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolHoverUndef(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#define [|PREPROCESSING_SYMBOL|] +#undef [|PRE$$PROCESSING_SYMBOL|] + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolHoverConst(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#Const [|PREPROCESS$$ING_SYMBOL|] = True + +#If [|PREPROCESSING_SYMBOL|] Then +' Some code +#End If + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestPreprocessingSymbolHoverAssignedConst(kind As TestKind, host As TestHost) As Task + Dim input = + + + +#Const [|PREPROCESSING_SYMBOL|] = True +#Const OTHER = [|PREPROCES$$SING_SYMBOL|] + +#If [|PREPROCESSING_SYMBOL|] Then +' Some code +#End If + + + + Await TestAPIAndFeature(input, kind, host) + End Function + End Class +End Namespace diff --git a/src/EditorFeatures/Test2/Formatting/FormattingCommandHandlerTests.vb b/src/EditorFeatures/Test2/Formatting/FormattingCommandHandlerTests.vb index e7467f0aa0195..2504dc7823b57 100644 --- a/src/EditorFeatures/Test2/Formatting/FormattingCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/Formatting/FormattingCommandHandlerTests.vb @@ -7,10 +7,9 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense ''' Tests that want to exercise as much of the real command handling stack as possible. ''' <[UseExportProvider]> + Public Class FormattingCommandHandlerTests - - - + Public Sub TypingUsingStatementsProperlyAligns1() Using state = TestStateFactory.CreateCSharpTestState( @@ -58,6 +57,28 @@ class TestClass End Using End Sub + + Public Sub TypingElseKeywordIndentsInAmbiguousScenario() + Using state = TestStateFactory.CreateCSharpTestState( + +using System; +class TestClass +{ + void TestMethod(string[] args) + { + foreach (var v in args) + if (v != null) + Console.WriteLine("v is not null"); + els$$ + } +} + , includeFormatCommandHandler:=True) + state.SendTypeChars("e") + + Assert.Equal(" else", state.GetLineTextFromCaretPosition()) + End Using + End Sub + Private Shared Sub AssertVirtualCaretColumn(state As TestState, expectedCol As Integer) Dim caretLine = state.GetLineFromCurrentCaretPosition() Dim caret = state.GetCaretPoint() diff --git a/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb b/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb index 0e2d5bb68980a..5fc5369d1ed53 100644 --- a/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb +++ b/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb @@ -855,6 +855,95 @@ class C Await TestAsync(workspace) End Function + + Public Async Function TestCSharpGoToOverriddenDefinition_FromOverride_LooseMatch() As Task + Dim workspace = + + + +class C +{ + public virtual void [|F|](bool x) + { + } +} + +class D : C +{ + public $$override void F(int x) + { + } +} + + + + + Await TestAsync(workspace) + End Function + + + Public Async Function TestCSharpGoToOverriddenDefinition_FromOverride_LooseMatch2() As Task + Dim workspace = + + + +class C +{ + public virtual void F() + { + } + + public virtual void [|F|](bool x) + { + } +} + +class D : C +{ + public $$override void F(int x) + { + } +} + + + + + Await TestAsync(workspace) + End Function + + + Public Async Function TestCSharpGoToOverriddenDefinition_FromOverride_LooseMatch3() As Task + Dim workspace = + + + +class B +{ + public virtual void F(bool x) + { + } +} + +class C +{ + public virtual void [|F|]() + { + } +} + +class D : C +{ + public $$override void F(int x) + { + } +} + + + + + Await TestAsync(workspace) + End Function + Public Async Function TestCSharpGoToUnmanaged_Keyword() As Task Dim workspace = diff --git a/src/EditorFeatures/Test2/GoToImplementation/GoToImplementationTests.vb b/src/EditorFeatures/Test2/GoToImplementation/GoToImplementationTests.vb index 3f745a8900df5..86ca0d5c2262c 100644 --- a/src/EditorFeatures/Test2/GoToImplementation/GoToImplementationTests.vb +++ b/src/EditorFeatures/Test2/GoToImplementation/GoToImplementationTests.vb @@ -11,7 +11,6 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToImplementation <[UseExportProvider]> Public Class GoToImplementationTests - Private Shared Async Function TestAsync(workspaceDefinition As XElement, host As TestHost, Optional shouldSucceed As Boolean = True, Optional metadataDefinitions As String() = Nothing) As Task Await GoToHelpers.TestAsync( workspaceDefinition, @@ -787,5 +786,30 @@ class D : C { public override void [|M|]() { } }} Await TestAsync(workspace, host) End Function + + + + Public Async Function FindLooseMatch1(host As TestHost) As Task + Dim workspace = + + + +class C +{ + public abstract void $$Foo() { } +} + +class D : C +{ + public override void [|Foo|](int i) + { + base.Foo(); + } +} + + + + Await TestAsync(workspace, host) + End Function End Class End Namespace diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index 6cf7e7776f03c..93e3d6a1af72e 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -16,7 +16,6 @@ Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletio Imports Microsoft.CodeAnalysis.Editor.Shared.Options Imports Microsoft.CodeAnalysis.Editor.[Shared].Utilities Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions -Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Options @@ -34,7 +33,7 @@ Imports Roslyn.Test.Utilities.TestGenerators Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense - Public Class CSharpCompletionCommandHandlerTests + Public NotInheritable Class CSharpCompletionCommandHandlerTests Public Async Function CompletionOnFileType_SameFile_NonQualified(showCompletionInArgumentLists As Boolean) As Task Using state = TestStateFactory.CreateCSharpTestState( @@ -12639,6 +12638,7 @@ $$ End Using End Function + Public Async Function CompletionInsideImplicitObjectCreationInsideCollectionExpression(showCompletionInArgumentLists As Boolean) As Task Using state = TestStateFactory.CreateCSharpTestState( @@ -12682,5 +12682,76 @@ internal class Program Await state.AssertCompletionItemsContain("Id", displayTextSuffix:="") End Using End Function + + + + Public Async Function PreferCamelCasedExactMatchOverPrefixCaseInsensitiveMatch1(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + + , + showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersion.CSharp12) + + state.SendTypeChars("CR") + Await state.AssertSelectedCompletionItem("CreateRange") + End Using + End Function + + + + Public Async Function PreferCamelCasedExactMatchOverPrefixCaseInsensitiveMatch2(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + + , + showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersion.CSharp12) + + state.SendTypeChars("cr") + Await state.AssertSelectedCompletionItem("Create") + End Using + End Function + + + + Public Async Function PreferCamelCasedExactMatchOverPrefixCaseInsensitiveMatch3(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + + class C + { + void Create() { } + void CreateRange() { } + + void M() + { + this.$$ + } + } + , + showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersion.CSharp12) + + state.SendTypeChars("Cr") + Await state.AssertSelectedCompletionItem("Create") + End Using + End Function End Class End Namespace diff --git a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb index 4550fce1c2846..c640397a1ce32 100644 --- a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests.vb @@ -3147,7 +3147,7 @@ Class C End Function - + Public Async Function TestMatchWithTurkishIWorkaround9() As Task Using New CultureContext(New Globalization.CultureInfo("tr-TR", useUserOverride:=False)) Using state = TestStateFactory.CreateVisualBasicTestState( @@ -3165,13 +3165,16 @@ Class C $$]]>) state.SendTypeChars("IF") Await state.WaitForAsynchronousOperationsAsync() - Await state.AssertSelectedCompletionItem("If") + + ' ICustomFormatter is better than `if` as the user used full-caps, which makes it more likely that + ' they wanted to camel case match + Await state.AssertSelectedCompletionItem("ICustomFormatter") End Using End Using End Function - + Public Async Function TestMatchWithTurkishIWorkaround10() As Task Using New CultureContext(New Globalization.CultureInfo("tr-TR", useUserOverride:=False)) Using state = TestStateFactory.CreateVisualBasicTestState( @@ -3190,7 +3193,10 @@ Class C ]]>) state.SendTypeChars("IF") Await state.WaitForAsynchronousOperationsAsync() - Await state.AssertSelectedCompletionItem("If") + + ' ICustomFormatter is better than `if` as the user used full-caps, which makes it more likely that + ' they wanted to camel case match + Await state.AssertSelectedCompletionItem("ICustomFormatter") End Using End Using End Function diff --git a/src/EditorFeatures/Test2/Rename/RenameCommandHandlerTests.vb b/src/EditorFeatures/Test2/Rename/RenameCommandHandlerTests.vb index f764b4ef590d1..19406ef40c3f5 100644 --- a/src/EditorFeatures/Test2/Rename/RenameCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/Rename/RenameCommandHandlerTests.vb @@ -6,7 +6,6 @@ Imports Microsoft.CodeAnalysis.Editor.Implementation.InlineRename Imports Microsoft.CodeAnalysis.Editor.InlineRename Imports Microsoft.CodeAnalysis.Editor.Shared.Extensions Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions -Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Text.Shared.Extensions Imports Microsoft.VisualStudio.Commanding diff --git a/src/EditorFeatures/Test2/Rename/RenameNonRenameableSymbols.vb b/src/EditorFeatures/Test2/Rename/RenameNonRenameableSymbols.vb index 270fc8854246c..d00714e271950 100644 --- a/src/EditorFeatures/Test2/Rename/RenameNonRenameableSymbols.vb +++ b/src/EditorFeatures/Test2/Rename/RenameNonRenameableSymbols.vb @@ -75,8 +75,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Rename End Using End Sub - - + Public Sub CannotRenameSpecialNames(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -97,8 +96,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Rename End Using End Sub - - + Public Sub CannotRenameTrivia(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -114,9 +112,10 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Rename End Using End Sub - - - Public Sub CannotRenameCandidateSymbol(host As RenameTestHost) + + + + Public Sub CanRenameCandidateSymbol(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -132,12 +131,11 @@ class Program , host) - AssertTokenNotRenamable(workspace) + AssertTokenRenamable(workspace) End Using End Sub - - + Public Sub CannotRenameSyntheticDefinition(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -158,8 +156,7 @@ class Program End Using End Sub - - + Public Sub CannotRenameXmlLiteralProperty(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -176,8 +173,7 @@ class Program End Using End Sub - - + Public Sub CannotRenameSymbolDefinedInMetaData(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -198,8 +194,7 @@ class Program End Using End Sub - - + Public Sub CannotRenameSymbolInReadOnlyBuffer(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -227,8 +222,7 @@ class Program End Using End Sub - - + Public Sub CannotRenameSymbolThatBindsToErrorType(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -249,8 +243,7 @@ class Program End Using End Sub - - + Public Sub CannotRenameSynthesizedParameters(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( @@ -635,8 +628,7 @@ namespace System End Sub - - + Public Sub RenameTupleFiledInLiteralRegress14600(host As RenameTestHost) Using workspace = CreateWorkspaceWithWaiter( diff --git a/src/EditorFeatures/Test2/Simplification/CastSimplificationTests.vb b/src/EditorFeatures/Test2/Simplification/CastSimplificationTests.vb index 9791137b56170..375418d345861 100644 --- a/src/EditorFeatures/Test2/Simplification/CastSimplificationTests.vb +++ b/src/EditorFeatures/Test2/Simplification/CastSimplificationTests.vb @@ -704,7 +704,7 @@ class C void M() { System.Action<string> g = null; - var h = (Goo<string>) + g; + var h = (Goo) + g; } static void Goo<T>(T y) { } diff --git a/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs b/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs index cad3d31dd1edb..6e9f9b7b8dcee 100644 --- a/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs +++ b/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs @@ -64,8 +64,8 @@ protected async Task TestAsync( var actualOrdered = actual.OrderBy((t1, t2) => t1.TextSpan.Start - t2.TextSpan.Start); - var actualFormatted = actualOrdered.Select(a => new FormattedClassification(allCode.Substring(a.TextSpan.Start, a.TextSpan.Length), a.ClassificationType)); - AssertEx.Equal(expected, actualFormatted); + var actualFormatted = actualOrdered.SelectAsArray(a => new FormattedClassification(allCode.Substring(a.TextSpan.Start, a.TextSpan.Length), a.ClassificationType)); + AssertEx.Equal(expected.ToImmutableArray(), actualFormatted); } private async Task TestAsync( diff --git a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs index c2ea493aaa752..3725fdab236d6 100644 --- a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs @@ -124,6 +124,11 @@ private protected abstract Task BaseVerifyWorkerAsync( List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false); + private protected abstract Task BaseVerifyWorkerAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, + SourceCodeKind sourceCodeKind, ItemExpectation[] expectedResults, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false); + internal Task GetCompletionListAsync( CompletionService service, Document document, @@ -143,6 +148,29 @@ private protected async Task CheckResultsAsync( List matchingFilters, CompletionItemFlags? flags, CompletionOptions options) + { + var expectedResult = new ItemExpectation( + Name: expectedItemOrNull, + IsAbsent: checkForAbsence, + ExpectedDescription: expectedDescriptionOrNull, + Glyph: glyph, + MatchPriority: matchPriority, + DisplayTextSuffix: displayTextSuffix, + DisplayTextPrefix: displayTextPrefix, + InlineDescription: inlineDescription, + IsComplexTextEdit: isComplexTextEdit); + + await CheckResultsAsync(document, position, usePreviousCharAsTrigger, deletedCharTrigger, + hasSuggestionModeItem, [expectedResult], matchingFilters, flags, options); + } + + private protected async Task CheckResultsAsync( + Document document, int position, bool usePreviousCharAsTrigger, + char? deletedCharTrigger, bool? hasSuggestionModeItem, + ItemExpectation[] expectedResults, + List matchingFilters, + CompletionItemFlags? flags, + CompletionOptions options) { options ??= GetCompletionOptions(); @@ -169,64 +197,105 @@ private protected async Task CheckResultsAsync( Assert.Equal(hasSuggestionModeItem.Value, completionList.SuggestionModeItem != null); } - if (checkForAbsence) + if (expectedResults.Length == 0) { - if (items == null) - { - return; - } + Assert.Equal(items.Count, 0); + } - if (expectedItemOrNull == null) + foreach (var result in expectedResults) + { + if (result.SourceCodeKind is not null && result.SourceCodeKind != document.SourceCodeKind) + continue; + + if (result.IsAbsent) { - Assert.Empty(items); + if (items == null) + { + return; + } + + if (result.Name == null) + { + Assert.Empty(items); + } + else + { + AssertEx.None( + items, + c => CompareItems(c.DisplayText, result.Name) + && CompareItems(c.DisplayTextSuffix, result.DisplayTextSuffix ?? "") + && CompareItems(c.DisplayTextPrefix, result.DisplayTextPrefix ?? "") + && CompareItems(c.InlineDescription, result.InlineDescription ?? "") + && (result.ExpectedDescription != null ? completionService.GetDescriptionAsync(document, c, options, displayOptions).Result.Text == result.ExpectedDescription : true)); + } } else { - AssertEx.None( - items, - c => CompareItems(c.DisplayText, expectedItemOrNull) - && CompareItems(c.DisplayTextSuffix, displayTextSuffix ?? "") - && CompareItems(c.DisplayTextPrefix, displayTextPrefix ?? "") - && CompareItems(c.InlineDescription, inlineDescription ?? "") - && (expectedDescriptionOrNull != null ? completionService.GetDescriptionAsync(document, c, options, displayOptions).Result.Text == expectedDescriptionOrNull : true)); - } - } - else - { - if (expectedItemOrNull == null) - { - Assert.NotEmpty(items); + if (result.Name == null) + { + Assert.NotEmpty(items); + } + else + { + AssertEx.Any(items, Predicate); + } } - else + + bool Predicate(RoslynCompletion.CompletionItem c) { - AssertEx.Any(items, Predicate); + if (!CompareItems(c.DisplayText, result.Name)) + return false; + if (!CompareItems(c.DisplayTextSuffix, result.DisplayTextSuffix ?? "")) + return false; + if (!CompareItems(c.DisplayTextPrefix, result.DisplayTextPrefix ?? "")) + return false; + if (!CompareItems(c.InlineDescription, result.InlineDescription ?? "")) + return false; + if (result.ExpectedDescription != null && completionService.GetDescriptionAsync(document, c, options, displayOptions).Result.Text != result.ExpectedDescription) + return false; + if (result.Glyph.HasValue && !c.Tags.SequenceEqual(GlyphTags.GetTags((Glyph)result.Glyph.Value))) + return false; + if (result.MatchPriority.HasValue && c.Rules.MatchPriority != result.MatchPriority.Value) + return false; + if (matchingFilters != null && !FiltersMatch(matchingFilters, c)) + return false; + if (flags != null && flags.Value != c.Flags) + return false; + if (result.IsComplexTextEdit is bool textEdit && textEdit != c.IsComplexTextEdit) + return false; + + return true; } } + } + + private protected record ItemExpectation( + string Name, + bool IsAbsent, + string ExpectedDescription = null, + int? Glyph = null, + int? MatchPriority = null, + string DisplayTextSuffix = null, + string DisplayTextPrefix = null, + string InlineDescription = null, + bool? IsComplexTextEdit = null, + SourceCodeKind? SourceCodeKind = null) + { + public static ItemExpectation[] None = CreateGeneralMatchingArray(true); + public static ItemExpectation[] Any = CreateGeneralMatchingArray(false); + + private static ItemExpectation[] CreateGeneralMatchingArray(bool absent) + { + return [new ItemExpectation(Name: null, IsAbsent: absent)]; + } - bool Predicate(RoslynCompletion.CompletionItem c) + public static ItemExpectation Exists(string name) { - if (!CompareItems(c.DisplayText, expectedItemOrNull)) - return false; - if (!CompareItems(c.DisplayTextSuffix, displayTextSuffix ?? "")) - return false; - if (!CompareItems(c.DisplayTextPrefix, displayTextPrefix ?? "")) - return false; - if (!CompareItems(c.InlineDescription, inlineDescription ?? "")) - return false; - if (expectedDescriptionOrNull != null && completionService.GetDescriptionAsync(document, c, options, displayOptions).Result.Text != expectedDescriptionOrNull) - return false; - if (glyph.HasValue && !c.Tags.SequenceEqual(GlyphTags.GetTags((Glyph)glyph.Value))) - return false; - if (matchPriority.HasValue && c.Rules.MatchPriority != matchPriority.Value) - return false; - if (matchingFilters != null && !FiltersMatch(matchingFilters, c)) - return false; - if (flags != null && flags.Value != c.Flags) - return false; - if (isComplexTextEdit is bool textEdit && textEdit != c.IsComplexTextEdit) - return false; - - return true; + return new ItemExpectation(name, false); + } + public static ItemExpectation Absent(string name) + { + return new ItemExpectation(name, true); } } @@ -246,7 +315,8 @@ private async Task VerifyAsync( string displayTextPrefix, string inlineDescription, bool? isComplexTextEdit, List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) { - foreach (var sourceKind in sourceCodeKind.HasValue ? [sourceCodeKind.Value] : new[] { SourceCodeKind.Regular, SourceCodeKind.Script }) + SourceCodeKind[] evaluatedSourceCodeKinds = sourceCodeKind.HasValue ? [sourceCodeKind.Value] : [SourceCodeKind.Regular, SourceCodeKind.Script]; + foreach (var sourceKind in evaluatedSourceCodeKinds) { using var workspaceFixture = GetOrCreateWorkspaceFixture(); @@ -266,6 +336,29 @@ await VerifyWorkerAsync( } } + private async Task VerifyAsync( + string markup, SourceCodeKind? sourceCodeKind, char? deletedCharTrigger, bool usePreviousCharAsTrigger, + ItemExpectation[] results, bool? hasSuggestionModeItem, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) + { + SourceCodeKind[] evaluatedSourceCodeKinds = sourceCodeKind.HasValue ? [sourceCodeKind.Value] : [SourceCodeKind.Regular, SourceCodeKind.Script]; + foreach (var sourceKind in evaluatedSourceCodeKinds) + { + using var workspaceFixture = GetOrCreateWorkspaceFixture(); + + var workspace = workspaceFixture.Target.GetWorkspace(markup, GetComposition()); + var code = workspaceFixture.Target.Code; + var position = workspaceFixture.Target.Position; + + // Set options that are not CompletionOptions + NonCompletionOptions?.SetGlobalOptions(workspace.GlobalOptions); + + await VerifyWorkerAsync( + code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionModeItem, + sourceKind, results, matchingFilters, flags, options, skipSpeculation); + } + } + protected async Task GetCompletionListAsync(string markup, string workspaceKind = null) { using var workspaceFixture = GetOrCreateWorkspaceFixture(); @@ -366,30 +459,35 @@ await VerifyAsync(markup, expectedItem, expectedDescriptionOrNull, sourceCodeKin } private protected async Task VerifyAnyItemExistsAsync( - string markup, SourceCodeKind? sourceCodeKind = null, bool usePreviousCharAsTrigger = false, char? deletedCharTrigger = null, - bool? hasSuggestionModeItem = null, string displayTextSuffix = null, string displayTextPrefix = null, - string inlineDescription = null, CompletionOptions options = null) + string markup, SourceCodeKind? sourceCodeKind = null, bool usePreviousCharAsTrigger = false, + bool? hasSuggestionModeItem = null, CompletionOptions options = null) { - await VerifyAsync(markup, expectedItemOrNull: null, expectedDescriptionOrNull: null, - sourceCodeKind, usePreviousCharAsTrigger: usePreviousCharAsTrigger, deletedCharTrigger: deletedCharTrigger, - checkForAbsence: false, glyph: null, matchPriority: null, - hasSuggestionModeItem: hasSuggestionModeItem, displayTextSuffix: displayTextSuffix, - displayTextPrefix: displayTextPrefix, inlineDescription: inlineDescription, - isComplexTextEdit: null, matchingFilters: null, flags: null, options); + await VerifyExpectedItemsAsync( + markup, results: ItemExpectation.Any, sourceCodeKind, usePreviousCharAsTrigger: usePreviousCharAsTrigger, + hasSuggestionModeItem: hasSuggestionModeItem, options: options); } private protected async Task VerifyNoItemsExistAsync( string markup, SourceCodeKind? sourceCodeKind = null, bool usePreviousCharAsTrigger = false, bool? hasSuggestionModeItem = null, - string displayTextSuffix = null, string inlineDescription = null, CompletionOptions options = null) + CompletionOptions options = null) { - await VerifyAsync( - markup, expectedItemOrNull: null, expectedDescriptionOrNull: null, - sourceCodeKind, usePreviousCharAsTrigger: usePreviousCharAsTrigger, deletedCharTrigger: null, - checkForAbsence: true, glyph: null, matchPriority: null, - hasSuggestionModeItem: hasSuggestionModeItem, displayTextSuffix: displayTextSuffix, - displayTextPrefix: null, inlineDescription: inlineDescription, - isComplexTextEdit: null, matchingFilters: null, flags: null, options); + await VerifyExpectedItemsAsync( + markup, results: ItemExpectation.None, sourceCodeKind, usePreviousCharAsTrigger: usePreviousCharAsTrigger, + hasSuggestionModeItem: hasSuggestionModeItem, options: options); + } + + private protected async Task VerifyExpectedItemsAsync( + string markup, ItemExpectation[] results, + SourceCodeKind? sourceCodeKind = null, + char? deletedCharTrigger = null, + bool usePreviousCharAsTrigger = false, + bool? hasSuggestionModeItem = null, CompletionOptions options = null) + { + await VerifyAsync(markup, + sourceCodeKind, deletedCharTrigger: deletedCharTrigger, usePreviousCharAsTrigger, results: results, + hasSuggestionModeItem: hasSuggestionModeItem, matchingFilters: null, + flags: null, options); } internal abstract Type GetCompletionProviderType(); @@ -413,6 +511,38 @@ private protected virtual async Task VerifyWorkerAsync( List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) + { + var expectedResult = new ItemExpectation( + Name: expectedItemOrNull, + IsAbsent: checkForAbsence, + ExpectedDescription: expectedDescriptionOrNull, + Glyph: glyph, + MatchPriority: matchPriority, + DisplayTextSuffix: displayTextSuffix, + DisplayTextPrefix: displayTextPrefix, + InlineDescription: inlineDescription, + IsComplexTextEdit: isComplexTextEdit); + + await VerifyWorkerCoreAsync( + code, position, usePreviousCharAsTrigger, deletedCharTrigger, + hasSuggestionModeItem, sourceCodeKind, [expectedResult], + matchingFilters, flags, options, skipSpeculation); + } + + /// + /// Override this to change parameters or return without verifying anything, e.g. for script sources. Or to test in other code contexts. + /// + /// The source code (not markup). + /// The expected results. If this is empty, verifies that item shows up for this CompletionProvider (or no items show up if checkForAbsence is true). + /// Whether or not the previous character in markup should be used to trigger IntelliSense for this provider. If false, invokes it through the invoke IntelliSense command. + private protected async Task VerifyWorkerCoreAsync( + string code, int position, bool usePreviousCharAsTrigger, + char? deletedCharTrigger, bool? hasSuggestionModeItem, SourceCodeKind sourceCodeKind, + ItemExpectation[] expectedResults, + List matchingFilters, + CompletionItemFlags? flags, + CompletionOptions options, + bool skipSpeculation = false) { using var workspaceFixture = GetOrCreateWorkspaceFixture(); @@ -420,23 +550,38 @@ private protected virtual async Task VerifyWorkerAsync( var document1 = workspaceFixture.Target.UpdateDocument(code, sourceCodeKind); await CheckResultsAsync( - document1, position, expectedItemOrNull, - expectedDescriptionOrNull, usePreviousCharAsTrigger, deletedCharTrigger, - checkForAbsence, glyph, matchPriority, - hasSuggestionModeItem, displayTextSuffix, displayTextPrefix, - inlineDescription, isComplexTextEdit, matchingFilters, flags, options); + document1, position, usePreviousCharAsTrigger, + deletedCharTrigger, hasSuggestionModeItem, expectedResults, + matchingFilters, flags, options); if (!skipSpeculation && await CanUseSpeculativeSemanticModelAsync(document1, position)) { var document2 = workspaceFixture.Target.UpdateDocument(code, sourceCodeKind, cleanBeforeUpdate: false); - await CheckResultsAsync( - document2, position, expectedItemOrNull, expectedDescriptionOrNull, - usePreviousCharAsTrigger, deletedCharTrigger, checkForAbsence, glyph, matchPriority, - hasSuggestionModeItem, displayTextSuffix, displayTextPrefix, - inlineDescription, isComplexTextEdit, matchingFilters, flags, options); + await CheckResultsAsync(document2, position, usePreviousCharAsTrigger, + deletedCharTrigger, hasSuggestionModeItem, expectedResults, + matchingFilters, flags, options); } } + /// + /// Override this to change parameters or return without verifying anything, e.g. for script sources. Or to test in other code contexts. + /// + /// The source code (not markup). + /// The expected results. If this is empty, verifies that item shows up for this CompletionProvider (or no items show up if checkForAbsence is true). + /// Whether or not the previous character in markup should be used to trigger IntelliSense for this provider. If false, invokes it through the invoke IntelliSense command. + private protected virtual Task VerifyWorkerAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, + bool? hasSuggestionModeItem, SourceCodeKind sourceCodeKind, + ItemExpectation[] expectedResults, + List matchingFilters, + CompletionItemFlags? flags, + CompletionOptions options, + bool skipSpeculation = false) + { + return VerifyWorkerCoreAsync(code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionModeItem, + sourceCodeKind, expectedResults, matchingFilters, flags, options, skipSpeculation); + } + /// /// Override this to change parameters or return without verifying anything, e.g. for script sources. Or to test in other code contexts. /// @@ -913,6 +1058,39 @@ protected async Task VerifyItemInLinkedFilesAsync(string xmlString, string expec } } + private protected async Task VerifyAtPositionAsync( + string code, int position, string insertText, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, SourceCodeKind sourceCodeKind, + ItemExpectation[] expectedResults, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) + { + code = code[..position] + insertText + code[position..]; + position += insertText.Length; + + await BaseVerifyWorkerAsync(code, position, usePreviousCharAsTrigger, + deletedCharTrigger, hasSuggestionItem, sourceCodeKind, expectedResults, + matchingFilters, flags, options, skipSpeculation: skipSpeculation); + } + + private protected async Task VerifyAtPositionAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, + SourceCodeKind sourceCodeKind, ItemExpectation[] expectedResults, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) + { + await VerifyAtPositionAsync(code, position, string.Empty, usePreviousCharAsTrigger, + deletedCharTrigger, hasSuggestionItem, sourceCodeKind, expectedResults, + matchingFilters, flags, options, skipSpeculation: skipSpeculation); + } + + private protected async Task VerifyAtPosition_ItemPartiallyWrittenAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, + SourceCodeKind sourceCodeKind, ItemExpectation[] expectedResults, string partialItem, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) + { + await VerifyAtPositionAsync(code, position, ItemPartiallyWritten(partialItem), + usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionItem, sourceCodeKind, expectedResults, + matchingFilters, flags, options, skipSpeculation: skipSpeculation); + } + private protected async Task VerifyAtPositionAsync( string code, int position, string insertText, bool usePreviousCharAsTrigger, char? deletedCharTrigger, string expectedItemOrNull, string expectedDescriptionOrNull, @@ -946,6 +1124,62 @@ await VerifyAtPositionAsync( inlineDescription, isComplexTextEdit, matchingFilters, flags, options, skipSpeculation: skipSpeculation); } + private protected async Task VerifyAtPosition_ItemPartiallyWrittenAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, + string expectedItemOrNull, string expectedDescriptionOrNull, + SourceCodeKind sourceCodeKind, bool checkForAbsence, int? glyph, + int? matchPriority, bool? hasSuggestionItem, string displayTextSuffix, + string displayTextPrefix, string inlineDescription = null, bool? isComplexTextEdit = null, + List matchingFilters = null, CompletionItemFlags? flags = null, + CompletionOptions options = null, bool skipSpeculation = false) + { + await VerifyAtPositionAsync( + code, position, ItemPartiallyWritten(expectedItemOrNull), usePreviousCharAsTrigger, + deletedCharTrigger, expectedItemOrNull, expectedDescriptionOrNull, sourceCodeKind, + checkForAbsence, glyph, matchPriority, hasSuggestionItem, displayTextSuffix, + displayTextPrefix, inlineDescription, isComplexTextEdit, matchingFilters, flags, options, + skipSpeculation: skipSpeculation); + } + + private protected async Task VerifyAtEndOfFileAsync( + string code, int position, string insertText, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, + SourceCodeKind sourceCodeKind, ItemExpectation[] expectedResults, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) + { + // only do this if the placeholder was at the end of the text. + if (code.Length != position) + { + return; + } + + code = code[..position] + insertText + code[position..]; + position += insertText.Length; + + await BaseVerifyWorkerAsync(code, position, + usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionItem, sourceCodeKind, expectedResults, + matchingFilters, flags, options, skipSpeculation: skipSpeculation); + } + + private protected async Task VerifyAtEndOfFileAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, + SourceCodeKind sourceCodeKind, ItemExpectation[] expectedResults, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options, bool skipSpeculation = false) + { + await VerifyAtEndOfFileAsync(code, position, string.Empty, + usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionItem, sourceCodeKind, expectedResults, + matchingFilters, flags, options, skipSpeculation: skipSpeculation); + } + + private protected async Task VerifyAtEndOfFileAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, + SourceCodeKind sourceCodeKind, ItemExpectation[] expectedResults, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options) + { + await VerifyAtEndOfFileAsync(code, position, string.Empty, + usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionItem, sourceCodeKind, expectedResults, + matchingFilters, flags, options); + } + private protected async Task VerifyAtEndOfFileAsync( string code, int position, string insertText, bool usePreviousCharAsTrigger, char? deletedCharTrigger, string expectedItemOrNull, string expectedDescriptionOrNull, @@ -971,23 +1205,6 @@ await BaseVerifyWorkerAsync( inlineDescription, isComplexTextEdit, matchingFilters, flags, options); } - private protected async Task VerifyAtPosition_ItemPartiallyWrittenAsync( - string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, - string expectedItemOrNull, string expectedDescriptionOrNull, - SourceCodeKind sourceCodeKind, bool checkForAbsence, int? glyph, - int? matchPriority, bool? hasSuggestionItem, string displayTextSuffix, - string displayTextPrefix, string inlineDescription = null, bool? isComplexTextEdit = null, - List matchingFilters = null, CompletionItemFlags? flags = null, - CompletionOptions options = null, bool skipSpeculation = false) - { - await VerifyAtPositionAsync( - code, position, ItemPartiallyWritten(expectedItemOrNull), usePreviousCharAsTrigger, deletedCharTrigger, - expectedItemOrNull, expectedDescriptionOrNull, sourceCodeKind, - checkForAbsence, glyph, matchPriority, hasSuggestionItem, displayTextSuffix, - displayTextPrefix, inlineDescription, isComplexTextEdit, matchingFilters, flags, options, - skipSpeculation: skipSpeculation); - } - private protected async Task VerifyAtEndOfFileAsync( string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, string expectedItemOrNull, string expectedDescriptionOrNull, @@ -1003,6 +1220,17 @@ await VerifyAtEndOfFileAsync(code, position, string.Empty, usePreviousCharAsTrig displayTextPrefix, inlineDescription, isComplexTextEdit, matchingFilters, flags, options); } + private protected async Task VerifyAtEndOfFile_ItemPartiallyWrittenAsync( + string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, bool? hasSuggestionItem, + SourceCodeKind sourceCodeKind, ItemExpectation[] expectedResults, string partialItem, + List matchingFilters, CompletionItemFlags? flags, CompletionOptions options) + { + await VerifyAtEndOfFileAsync( + code, position, ItemPartiallyWritten(partialItem), + usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionItem, sourceCodeKind, expectedResults, + matchingFilters, flags, options); + } + private protected async Task VerifyAtEndOfFile_ItemPartiallyWrittenAsync( string code, int position, bool usePreviousCharAsTrigger, char? deletedCharTrigger, string expectedItemOrNull, string expectedDescriptionOrNull, diff --git a/src/EditorFeatures/TestUtilities/DocumentationComments/AbstractDocumentationCommentTests.cs b/src/EditorFeatures/TestUtilities/DocumentationComments/AbstractDocumentationCommentTests.cs index cd63b1933cdea..25d59be39ec3c 100644 --- a/src/EditorFeatures/TestUtilities/DocumentationComments/AbstractDocumentationCommentTests.cs +++ b/src/EditorFeatures/TestUtilities/DocumentationComments/AbstractDocumentationCommentTests.cs @@ -162,7 +162,7 @@ private void Verify( MarkupTestFile.GetPosition(expectedMarkup, out var expectedCode, out int _); var actual = view.TextSnapshot.GetText(); - Assert.Equal(expectedCode, actual); + AssertEx.Equal(expectedCode, actual); var endCaretPosition = view.Caret.Position.BufferPosition.Position; var actualWithCaret = actual.Insert(endCaretPosition, "$$"); diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 5037ab8c5586d..ef7eda21bdaa2 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -102,9 +102,9 @@ public Task> MapSpansAsync(Document document, I } } - private protected class OrderLocations : Comparer + private protected class OrderLocations : Comparer { - public override int Compare(LSP.Location x, LSP.Location y) => CompareLocations(x, y); + public override int Compare(LSP.Location? x, LSP.Location? y) => CompareLocations(x, y); } protected virtual TestComposition Composition => EditorFeaturesLspComposition; @@ -148,8 +148,17 @@ private protected static void AssertLocationsEqual(IEnumerable exp AssertJsonEquals(orderedExpectedLocations, orderedActualLocations); } - private protected static int CompareLocations(LSP.Location l1, LSP.Location l2) + private protected static int CompareLocations(LSP.Location? l1, LSP.Location? l2) { + if (ReferenceEquals(l1, l2)) + return 0; + + if (l1 is null) + return -1; + + if (l2 is null) + return 1; + var compareDocument = l1.Uri.AbsoluteUri.CompareTo(l2.Uri.AbsoluteUri); var compareRange = CompareRange(l1.Range, l2.Range); return compareDocument != 0 ? compareDocument : compareRange; diff --git a/src/EditorFeatures/TestUtilities/StubSettingsManagerHost.cs b/src/EditorFeatures/TestUtilities/StubSettingsManagerHost.cs index d14ce4996f855..3b49070da8dcc 100644 --- a/src/EditorFeatures/TestUtilities/StubSettingsManagerHost.cs +++ b/src/EditorFeatures/TestUtilities/StubSettingsManagerHost.cs @@ -78,7 +78,7 @@ Task ISettingsManagerHost2.GetTelemetrySettingsAsync() throw new NotImplementedException(); } - Task ISettingsManagerHost5.GetServiceStreamAsync(string serviceMoniker, CancellationToken cancellationToken) + Task ISettingsManagerHost5.GetServiceStreamAsync(CancellationToken cancellationToken) { throw new NotImplementedException(); } diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.vb index 9d71a41dfd401..f7f5e9bd1e3c5 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.vb @@ -1626,6 +1626,37 @@ typeName:="Bar", isMissing:=True) End Function + + + Public Async Function GenerateType_GenericBaseList() As Task + Await TestWithMockedGenerateTypeDialog( +initial:= +Imports System.Collections.Generic + +Structure S + Implements IEnumerable(Of [|$$NewType|]) + +End Structure +.NormalizedValue, +languageName:=LanguageNames.VisualBasic, +typeName:="NewType", +expected:= +Imports System.Collections.Generic + +Structure S + Implements IEnumerable(Of NewType) + +End Structure + +Public Class NewType +End Class +.NormalizedValue, +isNewFile:=False, +accessibility:=Accessibility.Public, +typeKind:=TypeKind.Class, +assertGenerateTypeDialogOptions:=New GenerateTypeDialogOptions(False, TypeKindOptions.AllOptions, False)) + End Function + #End Region #Region "Delegates" diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb index 3d1737bdf71b2..e34ac4a375f62 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AbstractVisualBasicCompletionProviderTests.vb @@ -37,6 +37,15 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet isComplexTextEdit, matchingFilters, flags, options, skipSpeculation) End Function + Private Protected Overrides Function BaseVerifyWorkerAsync( + code As String, position As Integer, usePreviousCharAsTrigger As Boolean, deletedCharTrigger As Char?, + hasSuggestionItem As Boolean?, sourceCodeKind As SourceCodeKind, expectedResults() As ItemExpectation, + matchingFilters As List(Of CompletionFilter), flags As CompletionItemFlags?, options As CompletionOptions, Optional skipSpeculation As Boolean = False) As Task + Return MyBase.VerifyWorkerAsync( + code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionItem, sourceCodeKind, + expectedResults, matchingFilters, flags, options, skipSpeculation) + End Function + Private Protected Overrides Async Function VerifyWorkerAsync( code As String, position As Integer, expectedItemOrNull As String, expectedDescriptionOrNull As String, @@ -75,6 +84,41 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.Complet End If End Function + Private Protected Overrides Async Function VerifyWorkerAsync( + code As String, position As Integer, usePreviousCharAsTrigger As Boolean, deletedCharTrigger As Char?, + hasSuggestionModeItem As Boolean?, sourceCodeKind As SourceCodeKind, + expectedResults() As ItemExpectation, matchingFilters As List(Of CompletionFilter), + flags As CompletionItemFlags?, options As CompletionOptions, Optional skipSpeculation As Boolean = False) As Task + + ' Script/interactive support removed for now. + ' TODO: Re-enable these when interactive is back in the product. + If sourceCodeKind <> SourceCodeKind.Regular Then + Return + End If + + Await VerifyAtPositionAsync( + code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionModeItem, sourceCodeKind, + expectedResults, matchingFilters, flags, options, skipSpeculation) + + Await VerifyAtEndOfFileAsync( + code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionModeItem, sourceCodeKind, + expectedResults, matchingFilters, flags, options) + + ' Items cannot be partially written if we're checking for their absence, + ' or if we're verifying that the list will show up (without specifying an actual item) + For Each item In expectedResults + If Not item.IsAbsent AndAlso item.Name IsNot Nothing Then + Await VerifyAtPosition_ItemPartiallyWrittenAsync( + code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionModeItem, sourceCodeKind, + expectedResults, item.Name, matchingFilters, flags:=Nothing, options, skipSpeculation) + + Await VerifyAtEndOfFile_ItemPartiallyWrittenAsync( + code, position, usePreviousCharAsTrigger, deletedCharTrigger, hasSuggestionModeItem, sourceCodeKind, + expectedResults, item.Name, matchingFilters, flags:=Nothing, options) + End If + Next + + End Function Protected Overrides Async Function VerifyCustomCommitProviderWorkerAsync(codeBeforeCommit As String, position As Integer, itemToCommit As String, expectedCodeAfterCommit As String, sourceCodeKind As SourceCodeKind, Optional commitChar As Char? = Nothing) As Task ' Script/interactive support removed for now. ' TODO: Re-enable these when interactive is back in the product. diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpInstructionDecoder.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpInstructionDecoder.cs index ffb9693712bc5..bd7334fbd67cf 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpInstructionDecoder.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CSharpInstructionDecoder.cs @@ -34,6 +34,19 @@ private CSharpInstructionDecoder() AddMemberOptions(SymbolDisplayMemberOptions.IncludeParameters). WithParameterOptions(SymbolDisplayParameterOptions.IncludeType); + private static readonly SymbolDisplayFormat s_indexerCompactNameFormat = CompactNameFormat. + WithMemberOptions(SymbolDisplayMemberOptions.IncludeParameters). + WithParameterOptions(SymbolDisplayParameterOptions.None); + + internal override string GetCompactName(MethodSymbol method) + { + var symbol = method.AssociatedSymbol ?? method; + var format = symbol is PropertySymbol { IsIndexer: true } ? + s_indexerCompactNameFormat : + CompactNameFormat; + return symbol.ToDisplayString(format); + } + internal override void AppendFullName(StringBuilder builder, MethodSymbol method) { var displayFormat = method.MethodKind is MethodKind.PropertyGet or MethodKind.PropertySet diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs index e126c2e2e5096..5b50a64384805 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EEMethodSymbol.cs @@ -765,6 +765,6 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil return false; } - internal override int? TryGetOverloadResolutionPriority() => null; + internal override int TryGetOverloadResolutionPriority() => 0; } } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs index d8a443dfb53be..96aec08e8669b 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderMethodSymbol.cs @@ -285,7 +285,7 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol buil return false; } - internal override int? TryGetOverloadResolutionPriority() => null; + internal override int TryGetOverloadResolutionPriority() => 0; #if DEBUG protected override MethodSymbolAdapter CreateCciAdapter() diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/InstructionDecoderTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/InstructionDecoderTests.cs index ab55a95a123f1..296b53939144b 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/InstructionDecoderTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/InstructionDecoderTests.cs @@ -7,9 +7,9 @@ using System; using System.Diagnostics; using System.Linq; -using System.Reflection.Metadata.Ecma335; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; +using Microsoft.CodeAnalysis.CSharp.UnitTests; using Microsoft.CodeAnalysis.ExpressionEvaluator; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.PooledObjects; @@ -402,6 +402,144 @@ class C Assert.Equal("System.Action>", GetReturnTypeName(source, "C.M1", [typeof(object)])); } + [Fact] + public void GetCompactName_Members() + { + var source = """ + using System; + namespace System.Runtime.CompilerServices + { + public class IsExternalInit { } + } + class C + { + static C() { } + C(int x) { } + ~C() { } + object F() => null; + object P1 { get; } + object P2 { set { } } + object P3 { init { } } + object this[int i] { get { return null; } set { } } + event EventHandler E; + public static C operator+(C c) => c; + public static implicit operator int(C c) => 0; + public static explicit operator string(C c) => ""; + } + static class E + { + static void M(this string x) { } + } + """; + var compilation = CreateCompilation(source); + var containingType = compilation.GlobalNamespace.GetTypeMember("C"); + VerifyMethodName(compilation, containingType.GetMethod("F"), "C.F()", "F"); + VerifyMethodName(compilation, containingType.GetMethod("get_P1"), "C.P1.get()", "P1"); + VerifyMethodName(compilation, containingType.GetMethod("set_P2"), "C.P2.set(value)", "P2"); + VerifyMethodName(compilation, containingType.GetMethod("set_P3"), "C.P3.init(value)", "P3"); + VerifyMethodName(compilation, containingType.GetMethod("get_Item"), "C.this[int].get(i)", "this[]"); + VerifyMethodName(compilation, containingType.GetMethod("set_Item"), "C.this[int].set(i, value)", "this[]"); + VerifyMethodName(compilation, containingType.GetMethod("add_E"), "C.E.add(value)", "E"); + VerifyMethodName(compilation, containingType.GetMethod("remove_E"), "C.E.remove(value)", "E"); + VerifyMethodName(compilation, compilation.GlobalNamespace.GetTypeMember("E").GetMethod("M"), "E.M(x)", "M"); + VerifyMethodName(compilation, containingType.GetMethod(".cctor"), "C.C()", "C"); + VerifyMethodName(compilation, containingType.GetMethod(".ctor"), "C.C(x)", "C"); + VerifyMethodName(compilation, containingType.GetMethod("Finalize"), "C.~C()", "~C"); + VerifyMethodName(compilation, containingType.GetMethod("op_UnaryPlus"), "C.operator +(c)", "operator +"); + VerifyMethodName(compilation, containingType.GetMethod("op_Implicit"), "C.implicit operator int(c)", "implicit operator int"); + VerifyMethodName(compilation, containingType.GetMethod("op_Explicit"), "C.explicit operator string(c)", "explicit operator string"); + } + + [Fact] + public void GetCompactName_GenericMethod() + { + var source = """ + using System; + class A + { + public struct B + { + static void M(T t, U u, V v) { } + } + } + """; + var compilation = CreateCompilation(source); + var instructionDecoder = CSharpInstructionDecoder.Instance; + var method = GetConstructedMethod( + compilation, + (PEMethodSymbol)compilation.GlobalNamespace.GetTypeMember("A").GetTypeMember("B").GetMethod("M"), + ["object", "int", "string"], + instructionDecoder); + var actualName = instructionDecoder.GetName(method, includeParameterTypes: false, includeParameterNames: true); + var actualCompactName = instructionDecoder.GetCompactName(method); + Assert.Equal("A.B.M(t, u, v)", actualName); + Assert.Equal("M", actualCompactName); + } + + [Fact] + public void GetCompactName_ExplicitImplementation() + { + var source = """ + using System; + interface I + { + object F(); + object P { get; } + object this[int i] { get; } + event EventHandler E; + } + class C : I + { + object I.F() => null; + object I.P => null; + object I.this[int i] => null; + event EventHandler I.E { add { } remove { } } + } + """; + var compilation = CreateCompilation(source); + var containingType = compilation.GlobalNamespace.GetTypeMember("C"); + VerifyMethodName(compilation, containingType.GetMethod("I.F"), "C.I.F()", "I.F"); + VerifyMethodName(compilation, containingType.GetMethod("I.get_P"), "C.I.P.get()", "I.P"); + VerifyMethodName(compilation, containingType.GetMethod("I.get_Item"), "C.I.get_Item(i)", "I.get_Item"); + VerifyMethodName(compilation, containingType.GetMethod("I.add_E"), "C.I.E.add(value)", "I.E"); + } + + [Fact] + public void GetCompactName_NestedFunctions() + { + var source = """ + using System; + class Program + { + static void Main(string[] args) + { + Func f1 = () => args[0]; + string f2() => args[1]; + _ = f1(); + _ = f2(); + } + } + """; + var compilation = CreateCompilation(source); + var containingType = compilation.GlobalNamespace.GetTypeMember("Program").GetTypeMember("<>c__DisplayClass0_0"); + VerifyMethodName(compilation, containingType.GetMethod("
b__0"), "Program.Main.AnonymousMethod__0()", "
b__0"); + VerifyMethodName(compilation, containingType.GetMethod("
g__f2|1"), "Program.Main.__f2|1()", "
g__f2|1"); + } + + private void VerifyMethodName(CSharpCompilation compilation, MethodSymbol method, string expectedName, string expectedCompactName) + { + var instructionDecoder = CSharpInstructionDecoder.Instance; + method = GetConstructedMethod( + compilation, + (PEMethodSymbol)method, + null, + instructionDecoder); + var actualName = instructionDecoder.GetName(method, includeParameterTypes: false, includeParameterNames: true); + var actualCompactName = instructionDecoder.GetCompactName(method); + Assert.Equal(expectedName, actualName); + Assert.Equal(expectedCompactName, actualCompactName); + } + private string GetName(string source, string methodName, DkmVariableInfoFlags argumentFlags, Type[] typeArguments = null, string[] argumentValues = null) { var serializedTypeArgumentNames = typeArguments?.Select(t => t?.AssemblyQualifiedName).ToArray(); @@ -414,7 +552,12 @@ private string GetName(string source, string methodName, DkmVariableInfoFlags ar "Unexpected argumentFlags", "argumentFlags = {0}", argumentFlags); var instructionDecoder = CSharpInstructionDecoder.Instance; - var method = GetConstructedMethod(source, methodName, typeArguments, instructionDecoder); + var compilation = CreateCompilation(source); + var method = GetConstructedMethod( + compilation, + (PEMethodSymbol)GetMethodOrTypeBySignature(compilation, methodName), + typeArguments, + instructionDecoder); var includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types); var includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names); @@ -435,20 +578,27 @@ private string GetReturnTypeName(string source, string methodName, Type[] typeAr { var instructionDecoder = CSharpInstructionDecoder.Instance; var serializedTypeArgumentNames = typeArguments?.Select(t => t?.AssemblyQualifiedName).ToArray(); - var method = GetConstructedMethod(source, methodName, serializedTypeArgumentNames, instructionDecoder); + var compilation = CreateCompilation(source); + var method = GetConstructedMethod( + compilation, + (PEMethodSymbol)GetMethodOrTypeBySignature(compilation, methodName), + serializedTypeArgumentNames, + instructionDecoder); return instructionDecoder.GetReturnTypeName(method); } - private MethodSymbol GetConstructedMethod(string source, string methodName, string[] serializedTypeArgumentNames, CSharpInstructionDecoder instructionDecoder) + private CSharpCompilation CreateCompilation(string source) { var compilation = CreateCompilationWithMscorlib461(source, options: TestOptions.DebugDll, assemblyName: nameof(InstructionDecoderTests)); var runtime = CreateRuntimeInstance(compilation); var moduleInstances = runtime.Modules; var blocks = moduleInstances.SelectAsArray(m => m.MetadataBlock); - compilation = blocks.ToCompilation(default(Guid), MakeAssemblyReferencesKind.AllAssemblies); - var frame = (PEMethodSymbol)GetMethodOrTypeBySignature(compilation, methodName); + return blocks.ToCompilation(default(Guid), MakeAssemblyReferencesKind.AllAssemblies); + } + private MethodSymbol GetConstructedMethod(CSharpCompilation compilation, PEMethodSymbol frame, string[] serializedTypeArgumentNames, CSharpInstructionDecoder instructionDecoder) + { // Once we have the method token, we want to look up the method (again) // using the same helper as the product code. This helper will also map // async/iterator "MoveNext" methods to the original source method. diff --git a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/InstructionDecoder.cs b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/InstructionDecoder.cs index c8288ebf1beb5..ec86985d75dec 100644 --- a/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/InstructionDecoder.cs +++ b/src/ExpressionEvaluator/Core/Source/ExpressionCompiler/InstructionDecoder.cs @@ -23,6 +23,12 @@ internal abstract class InstructionDecoder + Public Sub GetCompactName_Members() + Dim source = " +Imports System +Imports System.Runtime.CompilerServices +Class C + Shared Sub New() + End Sub + Sub New(x As Integer) + End Sub + Function F() As Object + Return Nothing + End Function + Property P As Object + Get + Return Nothing + End Get + Set + End Set + End Property + Default Property Item(i As Integer) As Object + Get + Return Nothing + End Get + Set + End Set + End Property + Event E As EventHandler + Shared Operator +(c As C) As C + Return Nothing + End Operator + Shared Widening Operator CType(c As C) As String + Return Nothing + End Operator +End Class +Module E + + Sub M(x As String) + End Sub +End Module" + Dim compilation = CreateCompilation(source) + Dim containingType = compilation.GlobalNamespace.GetTypeMember("C") + VerifyMethodName(compilation, containingType.GetMethod("F"), "C.F()", "F") + VerifyMethodName(compilation, containingType.GetMethod("get_P"), "C.get_P()", "P") + VerifyMethodName(compilation, containingType.GetMethod("set_P"), "C.set_P(Value)", "P") + VerifyMethodName(compilation, containingType.GetMethod("get_Item"), "C.get_Item(i)", "Item") + VerifyMethodName(compilation, containingType.GetMethod("set_Item"), "C.set_Item(i, Value)", "Item") + VerifyMethodName(compilation, containingType.GetMethod("add_E"), "C.E(obj)", "E") + VerifyMethodName(compilation, containingType.GetMethod("remove_E"), "C.E(obj)", "E") + VerifyMethodName(compilation, containingType.GetMethod(".cctor"), "C.New()", "New") + VerifyMethodName(compilation, containingType.GetMethod(".ctor"), "C.New(x)", "New") + VerifyMethodName(compilation, containingType.GetMethod("op_UnaryPlus"), "C.+(c)", "+") + VerifyMethodName(compilation, containingType.GetMethod("op_Implicit"), "C.CType(c)", "CType") + VerifyMethodName(compilation, compilation.GlobalNamespace.GetTypeMember("E").GetMethod("M"), "E.M(x)", "M") + End Sub + + + Public Sub GetCompactName_GenericMethod() + Dim source = " +Imports System +Class A(Of T) + Public Structure B(Of U) + Sub M(Of V)(x As T, y As U, z As V) + End Sub + End Structure +End Class" + Dim compilation = CreateCompilation(source) + Dim instructionDecoder = VisualBasicInstructionDecoder.Instance + Dim method = GetConstructedMethod( + compilation, + DirectCast(compilation.GlobalNamespace.GetTypeMember("A").GetTypeMember("B").GetMethod("M"), PEMethodSymbol), + {"Object", "Integer", "String"}, + instructionDecoder) + Dim actualName = instructionDecoder.GetName(method, includeParameterTypes:=False, includeParameterNames:=True) + Dim actualCompactName = instructionDecoder.GetCompactName(method) + Assert.Equal("A(Of Object).B(Of Integer).M(Of String)(x, y, z)", actualName) + Assert.Equal("M", actualCompactName) + End Sub + + + Public Sub GetCompactName_ExplicitImplementation() + Dim source = " +Imports System +Interface I + Function F() As Object + Property P As Object + Default ReadOnly Property Item(i As Integer) As Object +End Interface +Class C + Implements I + Function I_F0() As Object Implements I.F + Return Nothing + End Function + Property I_P0 As Object Implements I.P + Get + Return Nothing + End Get + Set + End Set + End Property + Default ReadOnly Property I_Item0(i As Integer) As Object Implements I.Item + Get + Return Nothing + End Get + End Property +End Class" + Dim compilation = CreateCompilation(source) + Dim containingType = compilation.GlobalNamespace.GetTypeMember("C") + VerifyMethodName(compilation, containingType.GetMethod("I_F0"), "C.I_F0()", "I_F0") + VerifyMethodName(compilation, containingType.GetMethod("get_I_P0"), "C.get_I_P0()", "I_P0") + VerifyMethodName(compilation, containingType.GetMethod("set_I_P0"), "C.set_I_P0(Value)", "I_P0") + VerifyMethodName(compilation, containingType.GetMethod("get_I_Item0"), "C.get_I_Item0(i)", "I_Item0") + End Sub + + Private Sub VerifyMethodName(compilation As VisualBasicCompilation, method As MethodSymbol, expectedName As String, expectedCompactName As String) + Dim instructionDecoder = VisualBasicInstructionDecoder.Instance + method = GetConstructedMethod( + compilation, + DirectCast(method, PEMethodSymbol), + Nothing, + instructionDecoder) + Dim actualName = instructionDecoder.GetName(method, includeParameterTypes:=False, includeParameterNames:=True) + Dim actualCompactName = instructionDecoder.GetCompactName(method) + Assert.Equal(expectedName, actualName) + Assert.Equal(expectedCompactName, actualCompactName) + End Sub + Private Function GetName(source As String, methodName As String, argumentFlags As DkmVariableInfoFlags, Optional typeArguments() As Type = Nothing, Optional argumentValues() As String = Nothing) As String Dim serializedTypeArgumentNames = typeArguments?.Select(Function(t) t?.AssemblyQualifiedName).ToArray() Return GetName(source, methodName, argumentFlags, serializedTypeArgumentNames, argumentValues) @@ -474,7 +600,12 @@ End Class" "Unexpected argumentFlags", "argumentFlags = {0}", argumentFlags) Dim instructionDecoder = VisualBasicInstructionDecoder.Instance - Dim method = GetConstructedMethod(source, methodName, typeArguments, instructionDecoder) + Dim compilation = CreateCompilation(source) + Dim method = GetConstructedMethod( + compilation, + DirectCast(GetMethodOrTypeBySignature(compilation, methodName), PEMethodSymbol), + typeArguments, + instructionDecoder) Dim includeParameterTypes = argumentFlags.Includes(DkmVariableInfoFlags.Types) Dim includeParameterNames = argumentFlags.Includes(DkmVariableInfoFlags.Names) @@ -492,15 +623,7 @@ End Class" Return name End Function - Private Function GetReturnTypeName(source As String, methodName As String, Optional typeArguments() As Type = Nothing) As String - Dim instructionDecoder = VisualBasicInstructionDecoder.Instance - Dim serializedTypeArgumentNames = typeArguments?.Select(Function(t) t?.AssemblyQualifiedName).ToArray() - Dim method = GetConstructedMethod(source, methodName, serializedTypeArgumentNames, instructionDecoder) - - Return instructionDecoder.GetReturnTypeName(method) - End Function - - Private Function GetConstructedMethod(source As String, methodName As String, serializedTypeArgumentNames() As String, instructionDecoder As VisualBasicInstructionDecoder) As MethodSymbol + Private Function CreateCompilation(source As String) As VisualBasicCompilation Dim compilation = CreateEmptyCompilationWithReferences( {VisualBasicSyntaxTree.ParseText(source)}, references:={MscorlibRef_v4_0_30316_17626, MsvbRef_v4_0_30319_17929}, @@ -509,9 +632,23 @@ End Class" Dim runtime = CreateRuntimeInstance(compilation) Dim moduleInstances = runtime.Modules Dim blocks = moduleInstances.SelectAsArray(Function(m) m.MetadataBlock) - compilation = blocks.ToCompilation() - Dim frame = DirectCast(GetMethodOrTypeBySignature(compilation, methodName), PEMethodSymbol) + Return blocks.ToCompilation() + End Function + + Private Function GetReturnTypeName(source As String, methodName As String, Optional typeArguments() As Type = Nothing) As String + Dim instructionDecoder = VisualBasicInstructionDecoder.Instance + Dim serializedTypeArgumentNames = typeArguments?.Select(Function(t) t?.AssemblyQualifiedName).ToArray() + Dim compilation = CreateCompilation(source) + Dim method = GetConstructedMethod( + compilation, + DirectCast(GetMethodOrTypeBySignature(compilation, methodName), PEMethodSymbol), + serializedTypeArgumentNames, + instructionDecoder) + + Return instructionDecoder.GetReturnTypeName(method) + End Function + Private Function GetConstructedMethod(compilation As VisualBasicCompilation, frame As PEMethodSymbol, serializedTypeArgumentNames() As String, instructionDecoder As VisualBasicInstructionDecoder) As MethodSymbol ' Once we have the method token, we want to look up the method (again) ' using the same helper as the product code. This helper will also map ' async/ iterator "MoveNext" methods to the original source method. diff --git a/src/Features/CSharp/Portable/CodeRefactorings/ConvertLocalFunctionToMethod/CSharpConvertLocalFunctionToMethodCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/ConvertLocalFunctionToMethod/CSharpConvertLocalFunctionToMethodCodeRefactoringProvider.cs index da17567e32e99..4b12e0084164c 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/ConvertLocalFunctionToMethod/CSharpConvertLocalFunctionToMethodCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/ConvertLocalFunctionToMethod/CSharpConvertLocalFunctionToMethodCodeRefactoringProvider.cs @@ -264,11 +264,11 @@ private static List GenerateUniqueParameterNames(ImmutableArray GetReservedNames(SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken) => semanticModel.GetAllDeclaredSymbols(node.GetAncestor(), cancellationToken).Select(s => s.Name).ToList(); - private static ParameterSyntax GenerateParameter(IParameterSymbol p, string name) + private static ParameterSyntax GenerateParameter(IParameterSymbol parameter, string name) { return SyntaxFactory.Parameter(name.ToIdentifierToken()) - .WithModifiers(CSharpSyntaxGeneratorInternal.GetParameterModifiers(p.RefKind)) - .WithType(p.Type.GenerateTypeSyntax()); + .WithModifiers(CSharpSyntaxGeneratorInternal.GetParameterModifiers(parameter)) + .WithType(parameter.Type.GenerateTypeSyntax()); } private static MethodDeclarationSyntax WithBodyFrom( diff --git a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs index ab294eb81f9c0..624dfbb5bf2e4 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs @@ -25,19 +25,15 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.InlineTemporary; [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.InlineTemporary), Shared] -internal sealed partial class CSharpInlineTemporaryCodeRefactoringProvider +[method: ImportingConstructor] +[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] +internal sealed partial class CSharpInlineTemporaryCodeRefactoringProvider() : AbstractInlineTemporaryCodeRefactoringProvider { private static readonly SyntaxAnnotation DefinitionAnnotation = new(); private static readonly SyntaxAnnotation ReferenceAnnotation = new(); private static readonly SyntaxAnnotation ExpressionAnnotation = new(); - [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] - public CSharpInlineTemporaryCodeRefactoringProvider() - { - } - public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var (document, _, cancellationToken) = context; @@ -93,7 +89,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte context.RegisterRefactoring( CodeAction.Create( FeaturesResources.Inline_temporary_variable, - c => InlineTemporaryAsync(document, variableDeclarator, c), + cancellationToken => InlineTemporaryAsync(document, variableDeclarator, cancellationToken), nameof(FeaturesResources.Inline_temporary_variable)), variableDeclarator.Span); } @@ -191,12 +187,12 @@ private static async Task InlineTemporaryAsync(Document document, Vari var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); // Make each topmost parenting statement or Equals Clause Expressions semantically explicit. - document = await document.ReplaceNodesAsync(topmostParentingExpressions, (o, n) => + document = await document.ReplaceNodesAsync(topmostParentingExpressions, (original, current) => { // warn when inlining into a conditional expression, as the inlined expression will not be executed. - if (semanticModel.GetSymbolInfo(o, cancellationToken).Symbol is IMethodSymbol { IsConditional: true }) + if (semanticModel.GetSymbolInfo(original, cancellationToken).Symbol is IMethodSymbol { IsConditional: true }) { - n = n.WithAdditionalAnnotations( + current = current.WithAdditionalAnnotations( WarningAnnotation.Create(CSharpFeaturesResources.Warning_Inlining_temporary_into_conditional_method_call)); } @@ -204,12 +200,12 @@ private static async Task InlineTemporaryAsync(Document document, Vari // on the first inlined location. if (mayContainSideEffects) { - n = n.WithAdditionalAnnotations( + current = current.WithAdditionalAnnotations( WarningAnnotation.Create(CSharpFeaturesResources.Warning_Inlining_temporary_variable_may_change_code_meaning)); mayContainSideEffects = false; } - return n; + return current; }, cancellationToken).ConfigureAwait(false); return document; @@ -412,10 +408,14 @@ ExpressionSyntax CreateExpressionToInline() return SyntaxFactory.ArrayCreationExpression(arrayType, arrayInitializer); } - else if (isVar && expression is ObjectCreationExpressionSyntax or ArrayCreationExpressionSyntax or CastExpressionSyntax) + else if (isVar && expression is + ObjectCreationExpressionSyntax or + ArrayCreationExpressionSyntax or + CastExpressionSyntax or + InvocationExpressionSyntax) { // if we have `var x = new Y();` there's no need to do any casting as the type is indicated - // directly in the existing code. The same holds for `new Y[]` or `(Y)...` + // directly in the existing code. The same holds for `new Y[]` or `(Y)...` or `Y(...)` return expression; } else diff --git a/src/Features/CSharp/Portable/ConvertAnonymousType/CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertAnonymousType/CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs index cf3d02bb5eee2..4d16d9b6a335c 100644 --- a/src/Features/CSharp/Portable/ConvertAnonymousType/CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertAnonymousType/CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using System.Composition; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -16,34 +17,32 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertAnonymousType; using static SyntaxFactory; [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.ConvertAnonymousTypeToTuple), Shared] -internal class CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider +[method: ImportingConstructor] +[method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] +internal sealed class CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider() : AbstractConvertAnonymousTypeToTupleCodeRefactoringProvider< ExpressionSyntax, TupleExpressionSyntax, AnonymousObjectCreationExpressionSyntax> { - [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] - public CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider() - { - } - protected override int GetInitializerCount(AnonymousObjectCreationExpressionSyntax anonymousType) => anonymousType.Initializers.Count; protected override TupleExpressionSyntax ConvertToTuple(AnonymousObjectCreationExpressionSyntax anonCreation) => TupleExpression( - OpenParenToken.WithTriviaFrom(anonCreation.OpenBraceToken), - ConvertInitializers(anonCreation.Initializers), - CloseParenToken.WithTriviaFrom(anonCreation.CloseBraceToken)) - .WithPrependedLeadingTrivia(anonCreation.GetLeadingTrivia()); + OpenParenToken.WithTriviaFrom(anonCreation.OpenBraceToken), + ConvertInitializers(anonCreation.Initializers), + CloseParenToken.WithTriviaFrom(anonCreation.CloseBraceToken)) + .WithPrependedLeadingTrivia(anonCreation.GetLeadingTrivia()); private static SeparatedSyntaxList ConvertInitializers(SeparatedSyntaxList initializers) - => SeparatedList(initializers.Select(ConvertInitializer), initializers.GetSeparators()); + => SeparatedList(initializers.Select(ConvertInitializer), GetSeparators(initializers)); + + private static IEnumerable GetSeparators(SeparatedSyntaxList initializers) + => initializers.Count == 0 ? [] : initializers.GetSeparators().Take(initializers.Count - 1); private static ArgumentSyntax ConvertInitializer(AnonymousObjectMemberDeclaratorSyntax declarator) - => Argument(ConvertName(declarator.NameEquals), default, declarator.Expression) - .WithTriviaFrom(declarator); + => Argument(ConvertName(declarator.NameEquals), refKindKeyword: default, declarator.Expression).WithTriviaFrom(declarator); private static NameColonSyntax? ConvertName(NameEqualsSyntax? nameEquals) => nameEquals == null diff --git a/src/Features/CSharp/Portable/ConvertToInterpolatedString/CSharpConvertConcatenationToInterpolatedStringRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertToInterpolatedString/CSharpConvertConcatenationToInterpolatedStringRefactoringProvider.cs index 9a7cf2e5ebc2b..7c6d6a1f69ee5 100644 --- a/src/Features/CSharp/Portable/ConvertToInterpolatedString/CSharpConvertConcatenationToInterpolatedStringRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertToInterpolatedString/CSharpConvertConcatenationToInterpolatedStringRefactoringProvider.cs @@ -12,15 +12,11 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertToInterpolatedString; [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.ConvertConcatenationToInterpolatedString), Shared] -internal sealed class CSharpConvertConcatenationToInterpolatedStringRefactoringProvider : +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpConvertConcatenationToInterpolatedStringRefactoringProvider() : AbstractConvertConcatenationToInterpolatedStringRefactoringProvider { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpConvertConcatenationToInterpolatedStringRefactoringProvider() - { - } - protected override bool SupportsInterpolatedStringHandler(Compilation compilation) => compilation.GetTypeByMetadataName("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler") != null; diff --git a/src/Features/CSharp/Portable/EmbeddedLanguages/CSharpEmbeddedLanguagesProvider.cs b/src/Features/CSharp/Portable/EmbeddedLanguages/CSharpEmbeddedLanguagesProvider.cs index b9e463b2499b2..1b5fe90712f86 100644 --- a/src/Features/CSharp/Portable/EmbeddedLanguages/CSharpEmbeddedLanguagesProvider.cs +++ b/src/Features/CSharp/Portable/EmbeddedLanguages/CSharpEmbeddedLanguagesProvider.cs @@ -13,20 +13,16 @@ namespace Microsoft.CodeAnalysis.CSharp.EmbeddedLanguages.LanguageServices; [ExportLanguageService(typeof(IEmbeddedLanguagesProvider), LanguageNames.CSharp, ServiceLayer.Default), Shared] -internal class CSharpEmbeddedLanguagesProvider : AbstractEmbeddedLanguagesProvider +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpEmbeddedLanguagesProvider() : AbstractEmbeddedLanguagesProvider(Info) { public static readonly EmbeddedLanguageInfo Info = new( + CSharpBlockFacts.Instance, CSharpSyntaxFacts.Instance, CSharpSemanticFactsService.Instance, CSharpVirtualCharService.Instance); - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpEmbeddedLanguagesProvider() - : base(Info) - { - } - public override string EscapeText(string text, SyntaxToken token) => EmbeddedLanguageUtilities.EscapeText(text, token); } diff --git a/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs b/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs index 129b0945c52bf..468e73304c84f 100644 --- a/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs +++ b/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs @@ -664,23 +664,16 @@ internal override bool TryGetBaseList(ExpressionSyntax expression, out TypeKindO return false; } - var node = expression as SyntaxNode; - - while (node != null) + if (expression.Parent is BaseTypeSyntax { Parent: BaseListSyntax baseList }) { - if (node is BaseListSyntax) + if (baseList.Parent.Kind() is SyntaxKind.InterfaceDeclaration or SyntaxKind.StructDeclaration or SyntaxKind.RecordStructDeclaration) { - if (node.Parent.Kind() is SyntaxKind.InterfaceDeclaration or SyntaxKind.StructDeclaration or SyntaxKind.RecordStructDeclaration) - { - typeKindValue = TypeKindOptions.Interface; - return true; - } - - typeKindValue = TypeKindOptions.BaseList; + typeKindValue = TypeKindOptions.Interface; return true; } - node = node.Parent; + typeKindValue = TypeKindOptions.BaseList; + return true; } return false; diff --git a/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs b/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs index 24ce70acc8d8b..73b60a1c2c4a9 100644 --- a/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs +++ b/src/Features/CSharp/Portable/LanguageServices/CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -38,6 +39,15 @@ private sealed class SymbolDescriptionBuilder( private static readonly SymbolDisplayFormat s_minimallyQualifiedFormatWithConstantsAndModifiers = s_minimallyQualifiedFormatWithConstants .AddMemberOptions(SymbolDisplayMemberOptions.IncludeModifiers); + protected override SymbolDisplayFormat MinimallyQualifiedFormat + => s_minimallyQualifiedFormat; + + protected override SymbolDisplayFormat MinimallyQualifiedFormatWithConstants + => s_minimallyQualifiedFormatWithConstants; + + protected override SymbolDisplayFormat MinimallyQualifiedFormatWithConstantsAndModifiers + => s_minimallyQualifiedFormatWithConstantsAndModifiers; + protected override void AddDeprecatedPrefix() { AddToGroup(SymbolDescriptionGroups.MainDescription, @@ -103,7 +113,55 @@ protected override Task> GetInitializerSourceP } protected override ImmutableArray ToMinimalDisplayParts(ISymbol symbol, SemanticModel semanticModel, int position, SymbolDisplayFormat format) - => CodeAnalysis.CSharp.SymbolDisplay.ToMinimalDisplayParts(symbol, semanticModel, position, format); + { + var displayParts = CodeAnalysis.CSharp.SymbolDisplay.ToMinimalDisplayParts(symbol, semanticModel, position, format); + return WrapConstraints(symbol, displayParts); + } + + protected override ImmutableArray WrapConstraints(ISymbol symbol, ImmutableArray displayParts) + { + var typeParameter = symbol.GetTypeParameters(); + if (typeParameter.Length == 0) + return displayParts; + + // For readability, we add every 'where' on its own line if we have two or more constraints to wrap. + var wrappedConstraints = 0; + using var _ = ArrayBuilder.GetInstance(displayParts.Length, out var builder); + + var displayPartsSpans = displayParts.AsSpan(); + while (displayPartsSpans is [var firstSpan, ..]) + { + // Look for ` where T :` and add a line break before it. + if (displayPartsSpans is [ + { Kind: SymbolDisplayPartKind.Space }, + { Kind: SymbolDisplayPartKind.Keyword } keyword, + { Kind: SymbolDisplayPartKind.Space }, + { Kind: SymbolDisplayPartKind.TypeParameterName }, + { Kind: SymbolDisplayPartKind.Space }, + { Kind: SymbolDisplayPartKind.Punctuation } punctuation, + ..] && + keyword.ToString() == "where" && + punctuation.ToString() == ":") + { + // Intentionally do not this initial space. We want to replace it with a newline and 4 spaces instead. + + builder.AddRange(LineBreak()); + builder.AddRange(Space(4)); + wrappedConstraints++; + } + else + { + builder.Add(firstSpan); + } + + displayPartsSpans = displayPartsSpans[1..]; + } + + if (wrappedConstraints < 2) + return displayParts; + + return builder.ToImmutableAndClear(); + } protected override string? GetNavigationHint(ISymbol? symbol) => symbol == null ? null : CodeAnalysis.CSharp.SymbolDisplay.ToDisplayString(symbol, SymbolDisplayFormat.MinimallyQualifiedFormat); @@ -177,7 +235,7 @@ private async Task> GetInitializerSourcePartsA private async Task> GetInitializerSourcePartsAsync( EqualsValueClauseSyntax? equalsValue) { - if (equalsValue != null && equalsValue.Value != null) + if (equalsValue?.Value != null) { var semanticModel = GetSemanticModel(equalsValue.SyntaxTree); if (semanticModel != null) @@ -202,11 +260,5 @@ protected override void AddCaptures(ISymbol symbol) } } } - - protected override SymbolDisplayFormat MinimallyQualifiedFormat => s_minimallyQualifiedFormat; - - protected override SymbolDisplayFormat MinimallyQualifiedFormatWithConstants => s_minimallyQualifiedFormatWithConstants; - - protected override SymbolDisplayFormat MinimallyQualifiedFormatWithConstantsAndModifiers => s_minimallyQualifiedFormatWithConstantsAndModifiers; } } diff --git a/src/Features/CSharp/Portable/QuickInfo/CSharpSemanticQuickInfoProvider.cs b/src/Features/CSharp/Portable/QuickInfo/CSharpSemanticQuickInfoProvider.cs index 1dc07f0fe3df4..4ab387119518a 100644 --- a/src/Features/CSharp/Portable/QuickInfo/CSharpSemanticQuickInfoProvider.cs +++ b/src/Features/CSharp/Portable/QuickInfo/CSharpSemanticQuickInfoProvider.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.GoToDefinition; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.QuickInfo; @@ -151,6 +152,10 @@ protected override NullableFlowState GetNullabilityAnalysis(SemanticModel semant { return null; } + if (document.IsRazorDocument()) + { + return null; + } var symbolService = document.GetRequiredLanguageService(); var (symbol, _, _) = await symbolService.GetSymbolProjectAndBoundSpanAsync( diff --git a/src/Features/CSharp/Portable/Structure/CSharpBlockStructureProvider.cs b/src/Features/CSharp/Portable/Structure/CSharpBlockStructureProvider.cs index e06dd0d316c72..e918319709363 100644 --- a/src/Features/CSharp/Portable/Structure/CSharpBlockStructureProvider.cs +++ b/src/Features/CSharp/Portable/Structure/CSharpBlockStructureProvider.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Structure; -internal class CSharpBlockStructureProvider : AbstractBlockStructureProvider +internal sealed class CSharpBlockStructureProvider : AbstractBlockStructureProvider { private static ImmutableDictionary> CreateDefaultNodeProviderMap() { diff --git a/src/Features/CSharp/Portable/Structure/Providers/StringLiteralExpressionStructureProvider.cs b/src/Features/CSharp/Portable/Structure/Providers/StringLiteralExpressionStructureProvider.cs index 2e3f14d80f49d..4f73a74cf0940 100644 --- a/src/Features/CSharp/Portable/Structure/Providers/StringLiteralExpressionStructureProvider.cs +++ b/src/Features/CSharp/Portable/Structure/Providers/StringLiteralExpressionStructureProvider.cs @@ -18,35 +18,43 @@ protected override void CollectBlockSpans( BlockStructureOptions options, CancellationToken cancellationToken) { - if (node.IsKind(SyntaxKind.StringLiteralExpression) && - !node.ContainsDiagnostics && - CouldBeMultiLine()) + if (node.IsKind(SyntaxKind.StringLiteralExpression) && !node.ContainsDiagnostics) { - spans.Add(new BlockSpan( - isCollapsible: true, - textSpan: node.Span, - hintSpan: node.Span, - type: BlockTypes.Expression, - autoCollapse: true, - isDefaultCollapsed: false)); + var type = GetStringLiteralType(); + if (type != null) + { + spans.Add(new BlockSpan( + type, + isCollapsible: true, + textSpan: node.Span, + hintSpan: node.Span, + autoCollapse: true, + isDefaultCollapsed: false)); + } } return; - bool CouldBeMultiLine() + string? GetStringLiteralType() { + // We explicitly pick non-structural here as we don't want 'structure' guides shown for raw string literals. + // We already have a specialized tagger for those showing the user the left side of it. So having a + // structure guide as well is redundant. if (node.Token.Kind() is SyntaxKind.MultiLineRawStringLiteralToken or SyntaxKind.Utf8MultiLineRawStringLiteralToken) - return true; + return BlockTypes.Nonstructural; if (node.Token.IsVerbatimStringLiteral()) { var span = node.Span; var sourceText = node.SyntaxTree.GetText(cancellationToken); - return sourceText.Lines.GetLineFromPosition(span.Start).LineNumber != - sourceText.Lines.GetLineFromPosition(span.End).LineNumber; + if (sourceText.Lines.GetLineFromPosition(span.Start).LineNumber != + sourceText.Lines.GetLineFromPosition(span.End).LineNumber) + { + return BlockTypes.Expression; + } } - return false; + return null; } } } diff --git a/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs b/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs index 8a9422c4e0a2a..a14ed3349d2bb 100644 --- a/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs @@ -32,6 +32,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseAutoProperty; [method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] internal sealed partial class CSharpUseAutoPropertyCodeFixProvider() : AbstractUseAutoPropertyCodeFixProvider< + CSharpUseAutoPropertyCodeFixProvider, TypeDeclarationSyntax, PropertyDeclarationSyntax, VariableDeclaratorSyntax, @@ -130,8 +131,8 @@ protected override Task UpdatePropertyAsync( // Move any field initializer over to the property as well. if (fieldInitializer != null) { - updatedProperty = updatedProperty - .WithInitializer(EqualsValueClause(fieldInitializer)) + updatedProperty = updatedProperty.WithoutTrailingTrivia() + .WithInitializer(EqualsValueClause(EqualsToken.WithLeadingTrivia(Space).WithTrailingTrivia(Space), fieldInitializer)) .WithSemicolonToken(SemicolonToken); } diff --git a/src/Features/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.cs b/src/Features/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.cs index a0caafd2247db..b01cee51df531 100644 --- a/src/Features/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.cs +++ b/src/Features/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.cs @@ -9,12 +9,13 @@ using Microsoft.CodeAnalysis.CSharp.ConvertAnonymousType; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertAnonymousType; [Trait(Traits.Feature, Traits.Features.CodeActionsConvertAnonymousTypeToTuple)] -public partial class ConvertAnonymousTypeToTupleTests : AbstractCSharpCodeActionTest_NoEditor +public sealed class ConvertAnonymousTypeToTupleTests : AbstractCSharpCodeActionTest_NoEditor { protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWorkspace workspace, TestParameters parameters) => new CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider(); @@ -483,4 +484,45 @@ void Method() """; await TestInRegularAndScriptAsync(text, expected); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/34749")] + public async Task NotInExpressionTree() + { + await TestMissingInRegularAndScriptAsync(""" + using System.Linq.Expressions; + + class C + { + static void Main(string[] args) + { + Expression> test = + (par1, par2) => [||]new { Parameter1 = par1, Parameter2 = par2 }; + } + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75950")] + public async Task RemoveTrailingComma() + { + var text = """ + class Test + { + void Method() + { + var t1 = [||]new { a = 1, b = 2, }; + } + } + """; + var expected = """ + class Test + { + void Method() + { + var t1 = (a: 1, b: 2); + } + } + """; + await TestInRegularAndScriptAsync(text, expected); + } } diff --git a/src/Features/CSharpTest/ConvertCast/ConvertDirectCastToTryCastTests.cs b/src/Features/CSharpTest/ConvertCast/ConvertDirectCastToTryCastTests.cs index dcdd2d76d2b55..8347779d25a12 100644 --- a/src/Features/CSharpTest/ConvertCast/ConvertDirectCastToTryCastTests.cs +++ b/src/Features/CSharpTest/ConvertCast/ConvertDirectCastToTryCastTests.cs @@ -103,7 +103,6 @@ public static void Main() await new VerifyCS.Test { TestCode = InitialMarkup, - FixedCode = InitialMarkup, OffersEmptyRefactoring = false, CodeActionValidationMode = CodeActionValidationMode.None, }.RunAsync(); @@ -125,7 +124,6 @@ public void M() where T: struct await new VerifyCS.Test { TestCode = InitialMarkup, - FixedCode = InitialMarkup, OffersEmptyRefactoring = false, CodeActionValidationMode = CodeActionValidationMode.None, }.RunAsync(); @@ -147,7 +145,6 @@ public void M() await new VerifyCS.Test { TestCode = InitialMarkup, - FixedCode = InitialMarkup, OffersEmptyRefactoring = false, CodeActionValidationMode = CodeActionValidationMode.None, }.RunAsync(); diff --git a/src/Features/CSharpTest/ConvertCast/ConvertTryCastToDirectCastTests.cs b/src/Features/CSharpTest/ConvertCast/ConvertTryCastToDirectCastTests.cs index 2edc7508b99c5..3e9cd7af78c82 100644 --- a/src/Features/CSharpTest/ConvertCast/ConvertTryCastToDirectCastTests.cs +++ b/src/Features/CSharpTest/ConvertCast/ConvertTryCastToDirectCastTests.cs @@ -62,7 +62,6 @@ public static void Main() await new VerifyCS.Test { TestCode = InitialMarkup, - FixedCode = InitialMarkup, CompilerDiagnostics = CompilerDiagnostics.None, // CS0077 is present, but we present the refactoring anyway (this may overlap with a diagnostic fixer) OffersEmptyRefactoring = false, CodeActionValidationMode = CodeActionValidationMode.Full, @@ -84,7 +83,6 @@ public static void Main() await new VerifyCS.Test { TestCode = InitialMarkup, - FixedCode = InitialMarkup, CompilerDiagnostics = CompilerDiagnostics.None, OffersEmptyRefactoring = false, CodeActionValidationMode = CodeActionValidationMode.None, diff --git a/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs b/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs index d16e6a17d6fbd..dd2c121016149 100644 --- a/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs +++ b/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs @@ -2428,7 +2428,6 @@ void M(int i) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -2498,7 +2497,6 @@ void M(int i) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -2616,7 +2614,6 @@ void M(int i) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp8, }.RunAsync(); } @@ -2886,7 +2883,6 @@ void M(char c) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -2911,7 +2907,6 @@ int M(char c) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -2934,7 +2929,6 @@ void M(char c) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -2957,7 +2951,6 @@ void M(char c) await new VerifyCS.Test { TestCode = source, - FixedCode = source, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } diff --git a/src/Features/CSharpTest/ConvertLinq/ConvertLinqQueryToForEachTests.cs b/src/Features/CSharpTest/ConvertLinq/ConvertLinqQueryToForEachTests.cs index 0be23f6a32bdf..d271b75cda114 100644 --- a/src/Features/CSharpTest/ConvertLinq/ConvertLinqQueryToForEachTests.cs +++ b/src/Features/CSharpTest/ConvertLinq/ConvertLinqQueryToForEachTests.cs @@ -3353,7 +3353,7 @@ void M(IEnumerable nums) { for(int i = ([|from int n1 in nums from int n2 in nums - select n1|]).Count(), i < 5; i++) + select n1|]).Count(); i < 5; i++) { Console.WriteLine(i); } @@ -3369,20 +3369,17 @@ class C { void M(IEnumerable nums) { - IEnumerable enumerable() + for(int i = 0; i < 5; i++) { - foreach (int n1 in nums) - { - foreach (int n2 in nums) - { - yield return n1; - } - } + Console.WriteLine(i); } - for (int i = enumerable().Count(), i < 5; i++) + foreach (int n1 in nums) { - Console.WriteLine(i); + foreach (int n2 in nums) + { + i++; + } } } } diff --git a/src/Features/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs b/src/Features/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs index 37b1cc83d28ab..fa645a11949f8 100644 --- a/src/Features/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs +++ b/src/Features/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs @@ -36,7 +36,6 @@ namespace $$N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, Options = { @@ -56,7 +55,6 @@ namespace $$N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -119,7 +117,6 @@ namespace N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -139,7 +136,6 @@ namespace N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -163,7 +159,6 @@ namespace N2 await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -186,7 +181,6 @@ namespace N2 await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -209,7 +203,6 @@ namespace $$N2 await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -231,7 +224,6 @@ namespace $$N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -253,7 +245,6 @@ namespace $$N await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -623,7 +614,6 @@ namespace $$N; await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -684,7 +674,6 @@ namespace N; await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -703,7 +692,6 @@ namespace N; await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -721,7 +709,6 @@ namespace N; $$ await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { diff --git a/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainAnalyzerTests.cs b/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainAnalyzerTests.cs index 3d57289bb6c0e..929676950bda8 100644 --- a/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainAnalyzerTests.cs +++ b/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainAnalyzerTests.cs @@ -28,7 +28,6 @@ public async Task NotOfferedWhenUserPrefersTopLevelStatements() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true } }, @@ -51,7 +50,6 @@ void M() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, false } }, }.RunAsync(); @@ -139,7 +137,6 @@ public async Task NotOfferedInLibrary() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, false, NotificationOption2.Silent } }, }.RunAsync(); @@ -155,7 +152,6 @@ public async Task NotOfferedWhenSuppressed() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, false, NotificationOption2.None } }, diff --git a/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainRefactoringTests.cs b/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainRefactoringTests.cs index 6688cdae18cea..77cf9e04aaecb 100644 --- a/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainRefactoringTests.cs +++ b/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainRefactoringTests.cs @@ -31,7 +31,6 @@ class C await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, ExpectedDiagnostics = @@ -51,7 +50,6 @@ public async Task TestNotOnEmptyFile() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, ExpectedDiagnostics = @@ -93,7 +91,6 @@ public async Task TestNotOfferedInLibrary() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -133,7 +130,6 @@ public async Task TestNoConvertToProgramMainWithProgramMainPreferenceSuggestion( await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = @@ -152,7 +148,6 @@ public async Task TestNoConvertToProgramMainWithProgramMainPreferenceSilent() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = diff --git a/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsAnalyzerTests.cs b/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsAnalyzerTests.cs index 90a0ff9dac731..0043e5831dc3a 100644 --- a/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsAnalyzerTests.cs +++ b/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsAnalyzerTests.cs @@ -44,7 +44,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, false } }, @@ -67,7 +66,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp8, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true } }, @@ -246,7 +244,6 @@ static void Main() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true } }, }.RunAsync(); @@ -291,7 +288,6 @@ void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -319,7 +315,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -347,7 +342,6 @@ static void Main1(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -372,7 +366,6 @@ class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -394,7 +387,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -417,7 +409,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -440,7 +431,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -467,7 +457,6 @@ partial class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -490,7 +479,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -514,7 +502,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -538,7 +525,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -589,7 +575,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -615,7 +600,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -641,7 +625,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -667,7 +650,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -692,7 +674,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -717,7 +698,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -744,7 +724,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -771,7 +750,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -796,7 +774,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -821,7 +798,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -846,7 +822,6 @@ static void Main(string[] args) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, @@ -869,7 +844,6 @@ static void Main(string[] args1) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = { { CSharpCodeStyleOptions.PreferTopLevelStatements, true, NotificationOption2.Suggestion } }, diff --git a/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsRefactoringTests.cs b/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsRefactoringTests.cs index d429d6b707a7d..95721cc2dbbb9 100644 --- a/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsRefactoringTests.cs +++ b/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsRefactoringTests.cs @@ -31,7 +31,6 @@ public async Task TestNotOnEmptyFile() await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, ExpectedDiagnostics = @@ -59,7 +58,6 @@ class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, }.RunAsync(); @@ -108,7 +106,6 @@ class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -133,7 +130,6 @@ class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = @@ -165,7 +161,6 @@ class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp8, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = @@ -190,7 +185,6 @@ class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = @@ -215,7 +209,6 @@ class Program await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, TestState = { OutputKind = OutputKind.ConsoleApplication }, Options = diff --git a/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs b/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs index cfad68025c998..7756bb368d640 100644 --- a/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs +++ b/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs @@ -1024,7 +1024,6 @@ class C { LanguageVersion = LanguageVersion.CSharp9, TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -1081,7 +1080,6 @@ void M() { { LanguageVersion = LanguageVersion.CSharp9, TestCode = code, - FixedCode = code, }.RunAsync(); await new VerifyCS.Test @@ -1390,4 +1388,26 @@ public override string ToString() ReferenceAssemblies = ReferenceAssemblies.Net.Net60, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/61256")] + public async Task TestWithRawString() + { + await new VerifyCS.Test + { + TestCode = """" + struct ValueTuple + { + public void Goo() + { + var someVariable = "Some text"; + + var fullText = someVariable [||]+ """ + Appended line + """; + } + } + """", + LanguageVersion = LanguageVersion.CSharp11, + }.RunAsync(); + } } diff --git a/src/Features/CSharpTest/EmbeddedLanguages/JsonStringDetectorTests.cs b/src/Features/CSharpTest/EmbeddedLanguages/JsonStringDetectorTests.cs index ace78f9016557..192c6fc3a0e01 100644 --- a/src/Features/CSharpTest/EmbeddedLanguages/JsonStringDetectorTests.cs +++ b/src/Features/CSharpTest/EmbeddedLanguages/JsonStringDetectorTests.cs @@ -118,7 +118,6 @@ void Goo() await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } @@ -137,7 +136,6 @@ void Goo() await new VerifyCS.Test { TestCode = code, - FixedCode = code, }.RunAsync(); } } diff --git a/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs b/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs index eee1e1fd5efa7..bcf701a0ec174 100644 --- a/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs +++ b/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs @@ -545,7 +545,6 @@ public async Task DisabledIfSetInProject(NullableContextOptions nullableContextO await new VerifyCS.Test { TestCode = code, - FixedCode = code, SolutionTransforms = { (solution, projectId) => diff --git a/src/Features/CSharpTest/ExtractClass/ExtractClassTests.cs b/src/Features/CSharpTest/ExtractClass/ExtractClassTests.cs index 447e92479f21a..53100c1716a75 100644 --- a/src/Features/CSharpTest/ExtractClass/ExtractClassTests.cs +++ b/src/Features/CSharpTest/ExtractClass/ExtractClassTests.cs @@ -115,7 +115,6 @@ class Test : ErrorBase await new Test { TestCode = input, - FixedCode = input, }.RunAsync(); } @@ -135,7 +134,6 @@ class Test await new Test { TestCode = input, - FixedCode = input, WorkspaceKind = WorkspaceKind.MiscellaneousFiles }.RunAsync(); } @@ -432,7 +430,6 @@ record R(string $$S) await new Test { TestCode = input, - FixedCode = input, LanguageVersion = LanguageVersion.CSharp9, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -450,7 +447,6 @@ class R(string $$S) await new Test { TestCode = input, - FixedCode = input, LanguageVersion = LanguageVersion.CSharp12, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -466,7 +462,6 @@ class R(string $$S); await new Test { TestCode = input, - FixedCode = input, LanguageVersion = LanguageVersion.CSharp12, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -484,7 +479,6 @@ struct R(string $$S) await new Test { TestCode = input, - FixedCode = input, LanguageVersion = LanguageVersion.CSharp12, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -500,7 +494,6 @@ struct R(string $$S); await new Test { TestCode = input, - FixedCode = input, LanguageVersion = LanguageVersion.CSharp12, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -521,7 +514,6 @@ record struct R(string S) await new Test { TestCode = input, - FixedCode = input, LanguageVersion = LanguageVersion.CSharp10, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -542,7 +534,6 @@ struct R(string S) await new Test { TestCode = input, - FixedCode = input, LanguageVersion = LanguageVersion.CSharp12, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -2817,8 +2808,7 @@ class C """; await new Test { - TestCode = input, - FixedCode = input + TestCode = input }.RunAsync(); } @@ -2836,8 +2826,7 @@ class C """; await new Test { - TestCode = input, - FixedCode = input + TestCode = input }.RunAsync(); } @@ -2850,7 +2839,6 @@ public async Task TestTopLevelStatementSelection_NoAction() await new Test { TestCode = input, - FixedCode = input, LanguageVersion = LanguageVersion.CSharp10, TestState = { @@ -2987,8 +2975,7 @@ public void N() { }|] await new Test() { - TestCode = code, - FixedCode = code + TestCode = code }.RunAsync(); } @@ -3009,8 +2996,7 @@ public void N() { } await new Test() { - TestCode = code, - FixedCode = code + TestCode = code }.RunAsync(); } diff --git a/src/Features/CSharpTest/ExtractMethod/ExtractLocalFunctionTests.cs b/src/Features/CSharpTest/ExtractMethod/ExtractLocalFunctionTests.cs index bf1cefc748d8e..cc38ca4485195 100644 --- a/src/Features/CSharpTest/ExtractMethod/ExtractLocalFunctionTests.cs +++ b/src/Features/CSharpTest/ExtractMethod/ExtractLocalFunctionTests.cs @@ -590,7 +590,7 @@ class Program static void Main() { byte z = 0; - Goo({|Rename:NewMethod|}(), y => (byte)0, z, z); + Goo({|Rename:NewMethod|}(), y => 0, z, z); static Func NewMethod() { @@ -632,7 +632,7 @@ class Program static void Main() { byte z = 0; - Goo({|Rename:NewMethod|}(), y => { return (byte)0; }, z, z); + Goo({|Rename:NewMethod|}(), y => { return 0; }, z, z); static Func NewMethod() { diff --git a/src/Features/CSharpTest/ExtractMethod/ExtractMethodTests2.cs b/src/Features/CSharpTest/ExtractMethod/ExtractMethodTests2.cs index da95d9798d456..7492fb1be3169 100644 --- a/src/Features/CSharpTest/ExtractMethod/ExtractMethodTests2.cs +++ b/src/Features/CSharpTest/ExtractMethod/ExtractMethodTests2.cs @@ -840,7 +840,7 @@ class Program static void Main() { byte z = 0; - Goo({|Rename:NewMethod|}(), y => (byte)0, z, z); + Goo({|Rename:NewMethod|}(), y => 0, z, z); } private static Func NewMethod() @@ -882,7 +882,7 @@ class Program static void Main() { byte z = 0; - Goo({|Rename:NewMethod|}(), y => { return (byte)0; }, z, z); + Goo({|Rename:NewMethod|}(), y => { return 0; }, z, z); } private static Func NewMethod() diff --git a/src/Features/CSharpTest/GenerateFromMembers/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.cs b/src/Features/CSharpTest/GenerateFromMembers/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.cs index 3b5fa7bc1a794..9f908a3862680 100644 --- a/src/Features/CSharpTest/GenerateFromMembers/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.cs +++ b/src/Features/CSharpTest/GenerateFromMembers/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.cs @@ -1835,7 +1835,6 @@ public C() await new VerifyCS.Test { TestCode = source, - FixedCode = source, EditorConfig = FieldNamesCamelCaseWithFieldUnderscorePrefixEndUnderscoreSuffixEditorConfig }.RunAsync(); } diff --git a/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs b/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs index 93a70d5c9f7a8..33b14d1cc7e88 100644 --- a/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs +++ b/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs @@ -5842,4 +5842,73 @@ void SomeMethod(string _) { } await TestInRegularAndScriptAsync(code, expected); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/60552")] + public async Task TestNullable1() + { + var code = """ + #nullable enable + + public class C + { + private struct S + { + } + + public string M() + { + S s; + var [||]a = "" + s; // "Inline temporary variable" for a + return a; + } + } + """; + + var expected = """ + #nullable enable + + public class C + { + private struct S + { + } + + public string M() + { + S s; + // "Inline temporary variable" for a + return "" + s; + } + } + """; + + await TestInRegularAndScriptAsync(code, expected); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69869")] + public async Task InlineTemporaryNoNeededVariable() + { + await TestInRegularAndScriptAsync( + """ + using System; + class A + { + void M(string[] args) + { + var [||]a = Math.Round(1.1D); + var b = a; + } + } + """, + """ + using System; + class A + { + void M(string[] args) + { + var b = Math.Round(1.1D); + } + } + """); + } } diff --git a/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs b/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs index dd30dd77f6304..abe706c762c20 100644 --- a/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs +++ b/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs @@ -2624,7 +2624,7 @@ static void Main() { byte z = 0; Func {|Rename:p|} = x => 0; - Goo(p, y => (byte)0, z, z); + Goo(p, y => 0, z, z); } static void Goo(Func p, Func q, T r, S s) { Console.WriteLine(1); } diff --git a/src/Features/CSharpTest/SimplifyPropertyPattern/SimplifyPropertyPatternTests.cs b/src/Features/CSharpTest/SimplifyPropertyPattern/SimplifyPropertyPatternTests.cs index d9bb2f0109845..3d4befd345afc 100644 --- a/src/Features/CSharpTest/SimplifyPropertyPattern/SimplifyPropertyPatternTests.cs +++ b/src/Features/CSharpTest/SimplifyPropertyPattern/SimplifyPropertyPatternTests.cs @@ -42,7 +42,6 @@ void S(Type t) await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp9, }.RunAsync(); } @@ -108,7 +107,6 @@ void S(Type t) await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -134,7 +132,6 @@ void S(Type t) await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -160,7 +157,6 @@ void S(Type t) await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -186,7 +182,6 @@ void S(Type t) await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -212,7 +207,6 @@ void S(Type t) await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -238,7 +232,6 @@ void S(Type t) await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = LanguageVersion.CSharp10, }.RunAsync(); } @@ -750,7 +743,6 @@ void S(R r) await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = LanguageVersion.CSharp10, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); @@ -773,7 +765,6 @@ void S(R r) await new VerifyCS.Test { TestCode = testCode, - FixedCode = testCode, LanguageVersion = LanguageVersion.CSharp10, ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }.RunAsync(); diff --git a/src/Features/CSharpTest/UseNameofInAttribute/UseNameofInAttributeTests.cs b/src/Features/CSharpTest/UseNameofInAttribute/UseNameofInAttributeTests.cs index 21e46e4308899..fa81ca0737c9d 100644 --- a/src/Features/CSharpTest/UseNameofInAttribute/UseNameofInAttributeTests.cs +++ b/src/Features/CSharpTest/UseNameofInAttribute/UseNameofInAttributeTests.cs @@ -174,7 +174,6 @@ class C await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, ReferenceAssemblies = ReferenceAssemblies.Net.Net60, }.RunAsync(); @@ -196,7 +195,6 @@ class C await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11, ReferenceAssemblies = ReferenceAssemblies.Net.Net60, }.RunAsync(); @@ -218,7 +216,6 @@ class C await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11, ReferenceAssemblies = ReferenceAssemblies.Net.Net60, }.RunAsync(); @@ -240,7 +237,6 @@ class C await new VerifyCS.Test { TestCode = code, - FixedCode = code, LanguageVersion = LanguageVersion.CSharp11, ReferenceAssemblies = ReferenceAssemblies.Net.Net60, }.RunAsync(); diff --git a/src/Features/Core/Portable/Common/AbstractProjectExtensionProvider.cs b/src/Features/Core/Portable/Common/AbstractProjectExtensionProvider.cs index cf28b4a561435..b952df809e965 100644 --- a/src/Features/Core/Portable/Common/AbstractProjectExtensionProvider.cs +++ b/src/Features/Core/Portable/Common/AbstractProjectExtensionProvider.cs @@ -145,8 +145,10 @@ private ImmutableArray CreateExtensions(string language) if (TryGetExtensionsFromReference(this.Reference, out var extensions)) return extensions; + var analyzerFileReference = GetAnalyzerFileReference(this.Reference); + // otherwise, see whether we can pick it up from reference itself - if (this.Reference is not AnalyzerFileReference analyzerFileReference) + if (analyzerFileReference is null) return []; using var _ = ArrayBuilder.GetInstance(out var builder); @@ -183,5 +185,17 @@ private ImmutableArray CreateExtensions(string language) } return builder.ToImmutableAndClear(); + + static AnalyzerFileReference? GetAnalyzerFileReference(AnalyzerReference reference) + { + if (reference is AnalyzerFileReference analyzerFileReference) + return analyzerFileReference; +#if NET + if (reference is IsolatedAnalyzerFileReference isolatedReference) + return isolatedReference.UnderlyingAnalyzerFileReference; +#endif + + return null; + } } } diff --git a/src/Features/Core/Portable/Completion/CompletionHelper.cs b/src/Features/Core/Portable/Completion/CompletionHelper.cs index 82cf6c6c95b04..0273fbcd2a23d 100644 --- a/src/Features/Core/Portable/Completion/CompletionHelper.cs +++ b/src/Features/Core/Portable/Completion/CompletionHelper.cs @@ -124,13 +124,13 @@ private static int CompareItems( return expandedDiff; } - // Then see how the two items compare in a case insensitive fashion. Matches that - // are strictly better (ignoring case) should prioritize the item. i.e. if we have - // a prefix match, that should always be better than a substring match. + // Then see how the two items compare in a case insensitive fashion. Matches that are strictly better (ignoring + // case) should prioritize the item. i.e. if we have a prefix match, that should always be better than a + // substring match. // - // The reason we ignore case is that it's very common for people to type expecting - // completion to fix up their casing. i.e. 'false' will be written with the - // expectation that it will get fixed by the completion list to 'False'. + // The reason we ignore case is that it's very common for people to type expecting completion to fix up their + // casing. i.e. 'false' will be written with the expectation that it will get fixed by the completion list to + // 'False'. var caseInsensitiveComparison = match1.CompareTo(match2, ignoreCase: true); if (caseInsensitiveComparison != 0) { @@ -139,17 +139,18 @@ private static int CompareItems( // Now we have two items match in case-insensitive manner, // - // 1. if we are in a case-insensitive language, we'd first check if either item has the MatchPriority set to one of - // the two special values ("Preselect" and "Deprioritize"). If so and these two items have different MatchPriority, - // then we'd select the one of "Preselect", or the one that's not of "Deprioritize". Otherwise we will prefer the one - // matches case-sensitively. This is to make sure common items in VB like "True" and "False" are prioritized for selection - // when user types "t" and "f" (see https://github.com/dotnet/roslyn/issues/4892) + // 1. if we are in a case-insensitive language, we'd first check if either item has the MatchPriority set to one + // of the two special values ("Preselect" and "Deprioritize"). If so and these two items have different + // MatchPriority, then we'd select the one of "Preselect", or the one that's not of "Deprioritize". Otherwise + // we will prefer the one matches case-sensitively. This is to make sure common items in VB like "True" and + // "False" are prioritized for selection when user types "t" and "f" (see + // https://github.com/dotnet/roslyn/issues/4892) // - // 2. or similarly, if the filter text contains only lowercase letters, we want to relax our filtering standard a tiny - // bit to account for the sceanrio that users expect completion to fix the casing. This only happens if one of the item's - // MatchPriority is "Deprioritize". Otherwise we will always prefer the one matches case-sensitively. - // This is to make sure uncommon items like conversion "(short)" are not selected over `Should` when user types `sho` - // (see https://github.com/dotnet/roslyn/issues/55546) + // 2. or similarly, if the filter text contains only lowercase letters, we want to relax our filtering standard + // a tiny bit to account for the scenario that users expect completion to fix the casing. This only happens + // if one of the item's MatchPriority is "Deprioritize". Otherwise we will always prefer the one matches + // case-sensitively. This is to make sure uncommon items like conversion "(short)" are not selected over + // `Should` when user types `sho` (see https://github.com/dotnet/roslyn/issues/55546) var specialMatchPriorityValuesDiff = 0; if (!isCaseSensitive) diff --git a/src/Features/Core/Portable/Completion/CompletionService.cs b/src/Features/Core/Portable/Completion/CompletionService.cs index c3bfc799cf6e5..998b5562aab67 100644 --- a/src/Features/Core/Portable/Completion/CompletionService.cs +++ b/src/Features/Core/Portable/Completion/CompletionService.cs @@ -365,6 +365,19 @@ internal static void FilterItems( } } + internal static bool IsAllPunctuation(string filterText) + { + foreach (var ch in filterText) + { + if (!char.IsPunctuation(ch)) + { + return false; + } + } + + return true; + } + /// /// Don't call. Used for pre-populating MEF providers only. /// diff --git a/src/Features/Core/Portable/Completion/MatchResult.cs b/src/Features/Core/Portable/Completion/MatchResult.cs index c358a7e8d6833..7a91624fcbed0 100644 --- a/src/Features/Core/Portable/Completion/MatchResult.cs +++ b/src/Features/Core/Portable/Completion/MatchResult.cs @@ -80,4 +80,7 @@ public int Compare(MatchResult x, MatchResult y) return x.IndexInOriginalSortedOrder - y.IndexInOriginalSortedOrder; } } + + public override string ToString() + => this.CompletionItem.ToString(); } diff --git a/src/Features/Core/Portable/Contracts/EditAndContinue/IManagedHotReloadLanguageService.cs b/src/Features/Core/Portable/Contracts/EditAndContinue/IManagedHotReloadLanguageService.cs index aad37a672ebbb..3ca98a0430989 100644 --- a/src/Features/Core/Portable/Contracts/EditAndContinue/IManagedHotReloadLanguageService.cs +++ b/src/Features/Core/Portable/Contracts/EditAndContinue/IManagedHotReloadLanguageService.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -19,3 +20,9 @@ internal interface IManagedHotReloadLanguageService ValueTask OnCapabilitiesChangedAsync(CancellationToken cancellationToken); ValueTask StartSessionAsync(CancellationToken cancellationToken); } + +internal interface IManagedHotReloadLanguageService2 : IManagedHotReloadLanguageService +{ + ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken); + ValueTask UpdateBaselinesAsync(ImmutableArray projectPaths, CancellationToken cancellationToken); +} diff --git a/src/Features/Core/Portable/ConvertAnonymousType/AbstractConvertAnonymousTypeToTupleCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertAnonymousType/AbstractConvertAnonymousTypeToTupleCodeRefactoringProvider.cs index 5864f837f4bdb..42784975f2e1e 100644 --- a/src/Features/Core/Portable/ConvertAnonymousType/AbstractConvertAnonymousTypeToTupleCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertAnonymousType/AbstractConvertAnonymousTypeToTupleCodeRefactoringProvider.cs @@ -42,6 +42,11 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte return; var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var semanticFacts = document.GetRequiredLanguageService(); + + if (semanticFacts.IsInExpressionTree(semanticModel, anonymousNode, semanticModel.Compilation.ExpressionOfTType(), cancellationToken)) + return; + var allAnonymousNodes = GetAllAnonymousTypesInContainer(document, semanticModel, anonymousNode, cancellationToken); // If we have multiple different anonymous types in this member, then offer two fixes, one to just fixup this diff --git a/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertConcatenationToInterpolatedStringRefactoringProvider.cs b/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertConcatenationToInterpolatedStringRefactoringProvider.cs index 0b6cb035c8e7c..6da34202c6444 100644 --- a/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertConcatenationToInterpolatedStringRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertConcatenationToInterpolatedStringRefactoringProvider.cs @@ -80,6 +80,7 @@ public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContex return; var isVerbatimStringLiteral = false; + if (stringLiterals.Length > 0) { @@ -89,10 +90,18 @@ public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContex // escape sequences in these strings, so we only support combining the string // tokens if they're all the same type. var firstStringToken = stringLiterals[0].GetFirstToken(); + isVerbatimStringLiteral = syntaxFacts.IsVerbatimStringLiteral(firstStringToken); - for (int i = 1, n = stringLiterals.Length; i < n; i++) + + foreach (var literal in stringLiterals) { - if (isVerbatimStringLiteral != syntaxFacts.IsVerbatimStringLiteral(stringLiterals[i].GetFirstToken())) + var firstToken = literal.GetFirstToken(); + if (isVerbatimStringLiteral != syntaxFacts.IsVerbatimStringLiteral(firstToken)) + return; + + // Not currently supported with raw string literals due to the complexity of merging them and forming + // the final interpolated raw string. + if (syntaxFacts.IsRawStringLiteral(firstToken)) return; } } @@ -117,7 +126,10 @@ private async Task UpdateDocumentAsync( } protected async Task CreateInterpolatedStringAsync( - Document document, bool isVerbatimStringLiteral, ImmutableArray pieces, CancellationToken cancellationToken) + Document document, + bool isVerbatimStringLiteral, + ImmutableArray pieces, + CancellationToken cancellationToken) { var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var supportsInterpolatedStringHandler = this.SupportsInterpolatedStringHandler(semanticModel.Compilation); @@ -133,6 +145,7 @@ protected async Task CreateInterpolatedStringAsync( using var _ = ArrayBuilder.GetInstance(pieces.Length, out var content); var previousContentWasStringLiteralExpression = false; + foreach (var piece in pieces) { var isCharacterLiteral = syntaxFacts.IsCharacterLiteralExpression(piece); diff --git a/src/Features/Core/Portable/DocumentationComments/AbstractDocumentationCommentSnippetService.cs b/src/Features/Core/Portable/DocumentationComments/AbstractDocumentationCommentSnippetService.cs index c02d964a5992d..a4653c79e9698 100644 --- a/src/Features/Core/Portable/DocumentationComments/AbstractDocumentationCommentSnippetService.cs +++ b/src/Features/Core/Portable/DocumentationComments/AbstractDocumentationCommentSnippetService.cs @@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -39,8 +40,7 @@ internal abstract class AbstractDocumentationCommentSnippetService GetTargetMember(syntaxTree, text, position, cancellationToken) != null; + public bool IsValidTargetMember(ParsedDocument document, int position, CancellationToken cancellationToken) + => GetTargetMember(document, position, cancellationToken) != null; - private TMemberNode? GetTargetMember(SyntaxTree syntaxTree, SourceText text, int position, CancellationToken cancellationToken) + private TMemberNode? GetTargetMember(ParsedDocument document, int position, CancellationToken cancellationToken) { + var syntaxTree = document.SyntaxTree; + var text = document.Text; + var member = GetContainingMember(syntaxTree, position, cancellationToken); if (member == null) { @@ -226,7 +232,7 @@ private static void IndentLines(List lines, string? indentText) } } - public DocumentationCommentSnippet? GetDocumentationCommentSnippetOnEnterTyped(SyntaxTree syntaxTree, SourceText text, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken) + public DocumentationCommentSnippet? GetDocumentationCommentSnippetOnEnterTyped(ParsedDocument document, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken) { // Don't attempt to generate a new XML doc comment on ENTER if the option to auto-generate // them isn't set. Regardless of the option, we should generate exterior trivia (i.e. /// or ''') @@ -234,19 +240,20 @@ private static void IndentLines(List lines, string? indentText) if (options.AutoXmlDocCommentGeneration) { - var result = GenerateDocumentationCommentAfterEnter(syntaxTree, text, position, options, cancellationToken); + var result = GenerateDocumentationCommentAfterEnter(document, position, options, cancellationToken); if (result != null) - { return result; - } } - return GenerateExteriorTriviaAfterEnter(syntaxTree, text, position, options, cancellationToken); + return GenerateExteriorTriviaAfterEnter(document, position, options, cancellationToken); } - private DocumentationCommentSnippet? GenerateDocumentationCommentAfterEnter(SyntaxTree syntaxTree, SourceText text, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken) + private DocumentationCommentSnippet? GenerateDocumentationCommentAfterEnter(ParsedDocument document, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken) { // Find the documentation comment before the new line that was just pressed + var syntaxTree = document.SyntaxTree; + var text = document.Text; + var token = GetTokenToLeft(syntaxTree, position, cancellationToken); if (!IsDocCommentNewLine(token)) { @@ -286,9 +293,11 @@ private static void IndentLines(List lines, string? indentText) return new DocumentationCommentSnippet(replaceSpan, newText, offset); } - public DocumentationCommentSnippet? GetDocumentationCommentSnippetOnCommandInvoke(SyntaxTree syntaxTree, SourceText text, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken) + public DocumentationCommentSnippet? GetDocumentationCommentSnippetOnCommandInvoke(ParsedDocument document, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken) { - var targetMember = GetTargetMember(syntaxTree, text, position, cancellationToken); + var text = document.Text; + + var targetMember = GetTargetMember(document, position, cancellationToken); if (targetMember == null) { return null; @@ -323,9 +332,16 @@ private static void IndentLines(List lines, string? indentText) return new DocumentationCommentSnippet(replaceSpan, comments, offset); } - private DocumentationCommentSnippet? GenerateExteriorTriviaAfterEnter(SyntaxTree syntaxTree, SourceText text, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken) + private DocumentationCommentSnippet? GenerateExteriorTriviaAfterEnter(ParsedDocument document, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken) { + var syntaxTree = document.SyntaxTree; + var text = document.Text; + // Find the documentation comment before the new line that was just pressed + var syntaxFacts = document.LanguageServices.GetRequiredService(); + if (syntaxFacts.IsEntirelyWithinStringOrCharOrNumericLiteral(syntaxTree, position, cancellationToken)) + return null; + var token = GetTokenToLeft(syntaxTree, position, cancellationToken); if (!IsDocCommentNewLine(token) && HasSkippedTrailingTrivia(token)) { diff --git a/src/Features/Core/Portable/DocumentationComments/IDocumentationCommentSnippetService.cs b/src/Features/Core/Portable/DocumentationComments/IDocumentationCommentSnippetService.cs index a5adede4653ff..5eb3d9607cb70 100644 --- a/src/Features/Core/Portable/DocumentationComments/IDocumentationCommentSnippetService.cs +++ b/src/Features/Core/Portable/DocumentationComments/IDocumentationCommentSnippetService.cs @@ -16,23 +16,20 @@ internal interface IDocumentationCommentSnippetService : ILanguageService string DocumentationCommentCharacter { get; } DocumentationCommentSnippet? GetDocumentationCommentSnippetOnCharacterTyped( - SyntaxTree syntaxTree, - SourceText text, + ParsedDocument document, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken, bool addIndentation = true); DocumentationCommentSnippet? GetDocumentationCommentSnippetOnCommandInvoke( - SyntaxTree syntaxTree, - SourceText text, + ParsedDocument document, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken); DocumentationCommentSnippet? GetDocumentationCommentSnippetOnEnterTyped( - SyntaxTree syntaxTree, - SourceText text, + ParsedDocument document, int position, in DocumentationCommentOptions options, CancellationToken cancellationToken); @@ -42,5 +39,5 @@ internal interface IDocumentationCommentSnippetService : ILanguageService TextLine currentLine, TextLine previousLine); - bool IsValidTargetMember(SyntaxTree syntaxTree, SourceText text, int caretPosition, CancellationToken cancellationToken); + bool IsValidTargetMember(ParsedDocument document, int caretPosition, CancellationToken cancellationToken); } diff --git a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs index f528fb3a0fc29..88a1e4ee52bd1 100644 --- a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs @@ -53,7 +53,7 @@ internal sealed class DebuggingSession : IDisposable /// even when it's replaced in by a newer baseline. /// private readonly Dictionary _projectBaselines = []; - private readonly List _initialBaselineModuleReaders = []; + private readonly Dictionary _initialBaselineModuleReaders = []; private readonly object _projectEmitBaselinesGuard = new(); /// @@ -143,9 +143,13 @@ public void Dispose() // Wait for all operations on baseline to finish before we dispose the readers. _baselineAccessLock.EnterWriteLock(); - foreach (var reader in GetBaselineModuleReaders()) + lock (_projectEmitBaselinesGuard) { - reader.Dispose(); + foreach (var (_, readers) in _initialBaselineModuleReaders) + { + readers.metadata.Dispose(); + readers.pdb.Dispose(); + } } _baselineAccessLock.ExitWriteLock(); @@ -223,14 +227,6 @@ internal void RestartEditSession(ImmutableDictionary GetBaselineModuleReaders() - { - lock (_projectEmitBaselinesGuard) - { - return _initialBaselineModuleReaders.ToImmutableArrayOrEmpty(); - } - } - internal CompilationOutputs GetCompilationOutputs(Project project) => _compilationOutputsProvider(project); @@ -353,8 +349,7 @@ internal bool TryGetOrCreateEmitBaseline( baseline = new ProjectBaseline(baselineProject.Id, initialBaseline, generation: 0); _projectBaselines.Add(baselineProject.Id, baseline); - _initialBaselineModuleReaders.Add(metadataReaderProvider); - _initialBaselineModuleReaders.Add(debugInfoReaderProvider); + _initialBaselineModuleReaders.Add(baselineProject.Id, (metadataReaderProvider, debugInfoReaderProvider)); } return true; @@ -517,6 +512,7 @@ CommittedSolution.DocumentState.Indeterminate or public async ValueTask EmitSolutionUpdateAsync( Solution solution, + IImmutableSet runningProjects, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) { @@ -552,6 +548,14 @@ public async ValueTask EmitSolutionUpdateAsync( } } + EmitSolutionUpdateResults.GetProjectsToRebuildAndRestart( + solution, + solutionUpdate.ModuleUpdates, + rudeEditDiagnostics, + runningProjects, + out var projectsToRestart, + out var projectsToRebuild); + // Note that we may return empty deltas if all updates have been deferred. // The debugger will still call commit or discard on the update batch. return new EmitSolutionUpdateResults() @@ -561,6 +565,8 @@ public async ValueTask EmitSolutionUpdateAsync( Diagnostics = solutionUpdate.Diagnostics, RudeEdits = rudeEditDiagnostics.ToImmutable(), SyntaxError = solutionUpdate.SyntaxError, + ProjectsToRestart = projectsToRestart, + ProjectsToRebuild = projectsToRebuild }; } @@ -608,6 +614,39 @@ public void DiscardSolutionUpdate() _ = RetrievePendingUpdate(); } + public void UpdateBaselines(Solution solution, ImmutableArray rebuiltProjects) + { + ThrowIfDisposed(); + + // Make sure the solution snapshot has all source-generated documents up-to-date. + solution = solution.WithUpToDateSourceGeneratorDocuments(solution.ProjectIds); + + LastCommittedSolution.CommitSolution(solution); + + lock (_projectEmitBaselinesGuard) + { + foreach (var projectId in rebuiltProjects) + { + if (_projectBaselines.Remove(projectId)) + { + var (metadata, pdb) = _initialBaselineModuleReaders[projectId]; + metadata.Dispose(); + pdb.Dispose(); + + _initialBaselineModuleReaders.Remove(projectId); + } + } + } + + foreach (var projectId in rebuiltProjects) + { + _editSessionTelemetry.LogUpdatedBaseline(solution.GetRequiredProject(projectId).State.ProjectInfo.Attributes.TelemetryId); + } + + // Restart edit session reusing previous non-remappable regions and break state: + RestartEditSession(nonRemappableRegions: null, inBreakState: null); + } + /// /// Returns s for each document of , /// or default if not in a break state. @@ -851,31 +890,42 @@ internal TestAccessor GetTestAccessor() internal readonly struct TestAccessor(DebuggingSession instance) { - private readonly DebuggingSession _instance = instance; - public ImmutableHashSet GetModulesPreparedForUpdate() { - lock (_instance._modulesPreparedForUpdateGuard) + lock (instance._modulesPreparedForUpdateGuard) { - return [.. _instance._modulesPreparedForUpdate]; + return [.. instance._modulesPreparedForUpdate]; } } public EmitBaseline GetProjectEmitBaseline(ProjectId id) { - lock (_instance._projectEmitBaselinesGuard) + lock (instance._projectEmitBaselinesGuard) + { + return instance._projectBaselines[id].EmitBaseline; + } + } + + public bool HasProjectEmitBaseline(ProjectId id) + { + lock (instance._projectEmitBaselinesGuard) { - return _instance._projectBaselines[id].EmitBaseline; + return instance._projectBaselines.ContainsKey(id); } } public ImmutableArray GetBaselineModuleReaders() - => _instance.GetBaselineModuleReaders(); + { + lock (instance._projectEmitBaselinesGuard) + { + return instance._initialBaselineModuleReaders.Values.SelectMany(entry => new IDisposable[] { entry.metadata, entry.pdb }).ToImmutableArray(); + } + } public PendingUpdate? GetPendingSolutionUpdate() - => _instance._pendingUpdate; + => instance._pendingUpdate; public void SetTelemetryLogger(Action logger, Func getNextId) - => _instance._reportTelemetry = data => DebuggingSessionTelemetry.Log(data, logger, getNextId); + => instance._reportTelemetry = data => DebuggingSessionTelemetry.Log(data, logger, getNextId); } } diff --git a/src/Features/Core/Portable/EditAndContinue/DebuggingSessionTelemetry.cs b/src/Features/Core/Portable/EditAndContinue/DebuggingSessionTelemetry.cs index 2a9b478b6a437..32f2582d3db11 100644 --- a/src/Features/Core/Portable/EditAndContinue/DebuggingSessionTelemetry.cs +++ b/src/Features/Core/Portable/EditAndContinue/DebuggingSessionTelemetry.cs @@ -116,6 +116,9 @@ public static void Log(Data data, Action log, Func // Ids of all projects whose binaries were successfully updated during the session. map["ProjectIdsWithAppliedChanges"] = editSessionData.Committed ? editSessionData.ProjectsWithValidDelta.Select(ProjectIdToPii) : ""; + // Ids of all projects whose binaries had their initial baselines updated (the projects were rebuilt during debugging session). + map["ProjectIdsWithUpdatedBaselines"] = editSessionData.ProjectsWithUpdatedBaselines.Select(ProjectIdToPii); + // Total milliseconds it took to emit the delta in this edit session. map["EmitDifferenceMilliseconds"] = (long)editSessionData.EmitDifferenceTime.TotalMilliseconds; diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueService.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueService.cs index 80b9b57f741b5..7e4f13cce471c 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueService.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueService.cs @@ -221,6 +221,7 @@ public ValueTask> GetDocumentDiagnosticsAsync(Documen public ValueTask EmitSolutionUpdateAsync( DebuggingSessionId sessionId, Solution solution, + IImmutableSet runningProjects, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) { @@ -230,7 +231,7 @@ public ValueTask EmitSolutionUpdateAsync( return ValueTaskFactory.FromResult(EmitSolutionUpdateResults.Empty); } - return debuggingSession.EmitSolutionUpdateAsync(solution, activeStatementSpanProvider, cancellationToken); + return debuggingSession.EmitSolutionUpdateAsync(solution, runningProjects, activeStatementSpanProvider, cancellationToken); } public void CommitSolutionUpdate(DebuggingSessionId sessionId) @@ -249,6 +250,14 @@ public void DiscardSolutionUpdate(DebuggingSessionId sessionId) debuggingSession.DiscardSolutionUpdate(); } + public void UpdateBaselines(DebuggingSessionId sessionId, Solution solution, ImmutableArray rebuiltProjects) + { + var debuggingSession = TryGetDebuggingSession(sessionId); + Contract.ThrowIfNull(debuggingSession); + + debuggingSession.UpdateBaselines(solution, rebuiltProjects); + } + public ValueTask>> GetBaseActiveStatementSpansAsync(DebuggingSessionId sessionId, Solution solution, ImmutableArray documentIds, CancellationToken cancellationToken) { var debuggingSession = TryGetDebuggingSession(sessionId); diff --git a/src/Features/Core/Portable/EditAndContinue/EditSessionTelemetry.cs b/src/Features/Core/Portable/EditAndContinue/EditSessionTelemetry.cs index 73bc0540e4be0..d2ead3388ee92 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditSessionTelemetry.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditSessionTelemetry.cs @@ -18,6 +18,7 @@ internal readonly struct Data(EditSessionTelemetry telemetry) public readonly ImmutableArray<(ushort EditKind, ushort SyntaxKind, Guid projectId)> RudeEdits = telemetry._rudeEdits.AsImmutable(); public readonly ImmutableArray EmitErrorIds = telemetry._emitErrorIds.AsImmutable(); public readonly ImmutableArray ProjectsWithValidDelta = telemetry._projectsWithValidDelta.AsImmutable(); + public readonly ImmutableArray ProjectsWithUpdatedBaselines = telemetry._projectsWithUpdatedBaselines.AsImmutable(); public readonly EditAndContinueCapabilities Capabilities = telemetry._capabilities; public readonly bool HadCompilationErrors = telemetry._hadCompilationErrors; public readonly bool HadRudeEdits = telemetry._hadRudeEdits; @@ -38,6 +39,7 @@ internal readonly struct Data(EditSessionTelemetry telemetry) private readonly HashSet<(ushort, ushort, Guid)> _rudeEdits = []; private readonly HashSet _emitErrorIds = []; private readonly HashSet _projectsWithValidDelta = []; + private readonly HashSet _projectsWithUpdatedBaselines = []; private bool _hadCompilationErrors; private bool _hadRudeEdits; @@ -58,6 +60,7 @@ public Data GetDataAndClear() _rudeEdits.Clear(); _emitErrorIds.Clear(); _projectsWithValidDelta.Clear(); + _projectsWithUpdatedBaselines.Clear(); _hadCompilationErrors = false; _hadRudeEdits = false; _hadValidChanges = false; @@ -145,4 +148,7 @@ public void LogRuntimeCapabilities(EditAndContinueCapabilities capabilities) public void LogCommitted() => _committed = true; + + public void LogUpdatedBaseline(Guid projectTelemetryId) + => _projectsWithUpdatedBaselines.Add(projectTelemetryId); } diff --git a/src/Features/Core/Portable/EditAndContinue/EmitSolutionUpdateResults.cs b/src/Features/Core/Portable/EditAndContinue/EmitSolutionUpdateResults.cs index 7ca941eea35b5..bd159511970a6 100644 --- a/src/Features/Core/Portable/EditAndContinue/EmitSolutionUpdateResults.cs +++ b/src/Features/Core/Portable/EditAndContinue/EmitSolutionUpdateResults.cs @@ -2,16 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Globalization; using System.Linq; -using System.Runtime.ExceptionServices; using System.Runtime.Serialization; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Contracts.EditAndContinue; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; @@ -37,6 +32,12 @@ internal readonly struct Data [DataMember] public required DiagnosticData? SyntaxError { get; init; } + [DataMember] + public required ImmutableArray ProjectsToRestart { get; init; } + + [DataMember] + public required ImmutableArray ProjectsToRebuild { get; init; } + internal ImmutableArray GetAllDiagnostics() { using var _ = ArrayBuilder.GetInstance(out var builder); @@ -82,12 +83,14 @@ internal ImmutableArray GetAllDiagnostics() ModuleUpdates = new ModuleUpdates(ModuleUpdateStatus.None, []), Diagnostics = [], RudeEdits = [], - SyntaxError = null + SyntaxError = null, + ProjectsToRestart = [], + ProjectsToRebuild = [], }; /// /// Solution snapshot to resolve diagnostics in. - /// Note that this might be a different snapshot from the one passed to , + /// Note that this might be a different snapshot from the one passed to EmitSolutionUpdateAsync, /// with source generator files refreshed. /// /// Null only for empty results. @@ -99,6 +102,9 @@ internal ImmutableArray GetAllDiagnostics() public required ImmutableArray RudeEdits { get; init; } public required Diagnostic? SyntaxError { get; init; } + public required ImmutableArray ProjectsToRestart { get; init; } + public required ImmutableArray ProjectsToRebuild { get; init; } + public Data Dehydrate() => Solution == null ? new() @@ -106,14 +112,18 @@ public Data Dehydrate() ModuleUpdates = ModuleUpdates, Diagnostics = [], RudeEdits = [], - SyntaxError = null + SyntaxError = null, + ProjectsToRestart = [], + ProjectsToRebuild = [], } : new() { ModuleUpdates = ModuleUpdates, Diagnostics = Diagnostics.ToDiagnosticData(Solution), RudeEdits = RudeEdits.ToDiagnosticData(Solution), - SyntaxError = GetSyntaxErrorData() + SyntaxError = GetSyntaxErrorData(), + ProjectsToRestart = ProjectsToRestart, + ProjectsToRebuild = ProjectsToRebuild, }; private DiagnosticData? GetSyntaxErrorData() @@ -128,25 +138,19 @@ public Data Dehydrate() return DiagnosticData.Create(SyntaxError, Solution.GetRequiredDocument(SyntaxError.Location.SourceTree)); } - private IEnumerable GetProjectsContainingBlockingRudeEdits(Solution solution) - => RudeEdits - .Where(static e => e.Diagnostics.HasBlockingRudeEdits()) - .Select(static e => e.ProjectId) - .Distinct() - .OrderBy(static id => id) - .Select(solution.GetRequiredProject); - /// /// Returns projects that need to be rebuilt and/or restarted due to blocking rude edits in order to apply changes. /// - /// Identifies projects that have been launched. + /// Identifies projects that have been launched. /// Running projects that have to be restarted. /// Projects whose source have been updated and need to be rebuilt. - public void GetProjectsToRebuildAndRestart( + internal static void GetProjectsToRebuildAndRestart( Solution solution, - Func isRunningProject, - ISet projectsToRestart, - ISet projectsToRebuild) + ModuleUpdates moduleUpdates, + IEnumerable rudeEdits, + IImmutableSet runningProjects, + out ImmutableArray projectsToRestart, + out ImmutableArray projectsToRebuild) { var graph = solution.GetProjectDependencyGraph(); @@ -157,23 +161,24 @@ public void GetProjectsToRebuildAndRestart( // We need to repeat this process until we find a fixed point. using var _1 = ArrayBuilder.GetInstance(out var traversalStack); - - projectsToRestart.Clear(); - projectsToRebuild.Clear(); + using var _2 = PooledHashSet.GetInstance(out var projectsToRestartBuilder); + using var _3 = ArrayBuilder.GetInstance(out var projectsToRebuildBuilder); foreach (var projectWithRudeEdit in GetProjectsContainingBlockingRudeEdits(solution)) { - if (AddImpactedRunningProjects(projectsToRestart, projectWithRudeEdit)) + if (AddImpactedRunningProjects(projectsToRestartBuilder, projectWithRudeEdit)) { - projectsToRebuild.Add(projectWithRudeEdit); + projectsToRebuildBuilder.Add(projectWithRudeEdit.Id); } } // At this point the restart set contains all running projects directly affected by rude edits. // Next, find projects that were successfully updated and affect running projects. - if (ModuleUpdates.Updates.IsEmpty || projectsToRestart.IsEmpty()) + if (moduleUpdates.Updates.IsEmpty || projectsToRestartBuilder.Count == 0) { + projectsToRestart = [.. projectsToRestartBuilder]; + projectsToRebuild = [.. projectsToRebuildBuilder]; return; } @@ -185,14 +190,15 @@ public void GetProjectsToRebuildAndRestart( // If an updated project does not affect reset set in a given iteration, it stays in the set // because it may affect reset set later on, after another running project is added to it. - using var _2 = PooledHashSet.GetInstance(out var updatedProjects); - using var _3 = ArrayBuilder.GetInstance(out var updatedProjectsToRemove); - foreach (var update in ModuleUpdates.Updates) + using var _4 = PooledHashSet.GetInstance(out var updatedProjects); + using var _5 = ArrayBuilder.GetInstance(out var updatedProjectsToRemove); + + foreach (var update in moduleUpdates.Updates) { updatedProjects.Add(solution.GetRequiredProject(update.ProjectId)); } - using var _4 = ArrayBuilder.GetInstance(out var impactedProjects); + using var _6 = ArrayBuilder.GetInstance(out var impactedProjects); while (true) { @@ -201,11 +207,11 @@ public void GetProjectsToRebuildAndRestart( foreach (var updatedProject in updatedProjects) { if (AddImpactedRunningProjects(impactedProjects, updatedProject) && - impactedProjects.Any(projectsToRestart.Contains)) + impactedProjects.Any(projectsToRestartBuilder.Contains)) { - projectsToRestart.AddRange(impactedProjects); + projectsToRestartBuilder.AddRange(impactedProjects); updatedProjectsToRemove.Add(updatedProject); - projectsToRebuild.Add(updatedProject); + projectsToRebuildBuilder.Add(updatedProject.Id); } impactedProjects.Clear(); @@ -221,9 +227,11 @@ public void GetProjectsToRebuildAndRestart( updatedProjectsToRemove.Clear(); } + projectsToRestart = [.. projectsToRestartBuilder]; + projectsToRebuild = [.. projectsToRebuildBuilder]; return; - bool AddImpactedRunningProjects(ICollection impactedProjects, Project initialProject) + bool AddImpactedRunningProjects(ICollection impactedProjects, Project initialProject) { Debug.Assert(traversalStack.Count == 0); traversalStack.Push(initialProject); @@ -233,9 +241,9 @@ bool AddImpactedRunningProjects(ICollection impactedProjects, Project i while (traversalStack.Count > 0) { var project = traversalStack.Pop(); - if (isRunningProject(project)) + if (runningProjects.Contains(project.Id)) { - impactedProjects.Add(project); + impactedProjects.Add(project.Id); added = true; } @@ -247,6 +255,14 @@ bool AddImpactedRunningProjects(ICollection impactedProjects, Project i return added; } + + IEnumerable GetProjectsContainingBlockingRudeEdits(Solution solution) + => rudeEdits + .Where(static e => e.Diagnostics.HasBlockingRudeEdits()) + .Select(static e => e.ProjectId) + .Distinct() + .OrderBy(static id => id) + .Select(solution.GetRequiredProject); } public ImmutableArray GetAllDiagnostics() diff --git a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueService.cs b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueService.cs index 32c1360a2d976..cc39e13802a7f 100644 --- a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueService.cs +++ b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueService.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -19,10 +20,11 @@ internal interface IEditAndContinueWorkspaceService : IWorkspaceService internal interface IEditAndContinueService { ValueTask> GetDocumentDiagnosticsAsync(Document document, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken); - ValueTask EmitSolutionUpdateAsync(DebuggingSessionId sessionId, Solution solution, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken); + ValueTask EmitSolutionUpdateAsync(DebuggingSessionId sessionId, Solution solution, IImmutableSet runningProjects, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken); void CommitSolutionUpdate(DebuggingSessionId sessionId); void DiscardSolutionUpdate(DebuggingSessionId sessionId); + void UpdateBaselines(DebuggingSessionId sessionId, Solution solution, ImmutableArray rebuiltProjects); ValueTask StartDebuggingSessionAsync(Solution solution, IManagedHotReloadService debuggerService, IPdbMatchingSourceTextProvider sourceTextProvider, ImmutableArray captureMatchingDocuments, bool captureAllMatchingDocuments, bool reportDiagnostics, CancellationToken cancellationToken); void BreakStateOrCapabilitiesChanged(DebuggingSessionId sessionId, bool? inBreakState); diff --git a/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs b/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs index be246e9a748e4..7b60868698964 100644 --- a/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs +++ b/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs @@ -27,13 +27,14 @@ internal interface ICallback } ValueTask> GetDocumentDiagnosticsAsync(Checksum solutionChecksum, RemoteServiceCallbackId callbackId, DocumentId documentId, CancellationToken cancellationToken); - ValueTask EmitSolutionUpdateAsync(Checksum solutionChecksum, RemoteServiceCallbackId callbackId, DebuggingSessionId sessionId, CancellationToken cancellationToken); + ValueTask EmitSolutionUpdateAsync(Checksum solutionChecksum, RemoteServiceCallbackId callbackId, DebuggingSessionId sessionId, IImmutableSet runningProjects, CancellationToken cancellationToken); /// /// Returns ids of documents for which diagnostics need to be refreshed in-proc. /// ValueTask CommitSolutionUpdateAsync(DebuggingSessionId sessionId, CancellationToken cancellationToken); ValueTask DiscardSolutionUpdateAsync(DebuggingSessionId sessionId, CancellationToken cancellationToken); + ValueTask UpdateBaselinesAsync(Checksum solutionInfo, DebuggingSessionId sessionId, ImmutableArray rebuiltProjects, CancellationToken cancellationToken); ValueTask StartDebuggingSessionAsync(Checksum solutionChecksum, RemoteServiceCallbackId callbackId, ImmutableArray captureMatchingDocuments, bool captureAllMatchingDocuments, bool reportDiagnostics, CancellationToken cancellationToken); diff --git a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs index dfb2d42af2407..376c21819f30c 100644 --- a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs +++ b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -55,6 +56,7 @@ await client.TryInvokeAsync( public async ValueTask EmitSolutionUpdateAsync( Solution solution, + IImmutableSet runningProjects, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) { @@ -63,12 +65,12 @@ await client.TryInvokeAsync( var client = await RemoteHostClient.TryGetClientAsync(services, cancellationToken).ConfigureAwait(false); if (client == null) { - return (await GetLocalService().EmitSolutionUpdateAsync(sessionId, solution, activeStatementSpanProvider, cancellationToken).ConfigureAwait(false)).Dehydrate(); + return (await GetLocalService().EmitSolutionUpdateAsync(sessionId, solution, runningProjects, activeStatementSpanProvider, cancellationToken).ConfigureAwait(false)).Dehydrate(); } var result = await client.TryInvokeAsync( solution, - (service, solutionInfo, callbackId, cancellationToken) => service.EmitSolutionUpdateAsync(solutionInfo, callbackId, sessionId, cancellationToken), + (service, solutionInfo, callbackId, cancellationToken) => service.EmitSolutionUpdateAsync(solutionInfo, callbackId, sessionId, runningProjects, cancellationToken), callbackTarget: new ActiveStatementSpanProviderCallback(activeStatementSpanProvider), cancellationToken).ConfigureAwait(false); @@ -78,6 +80,8 @@ await client.TryInvokeAsync( Diagnostics = [], RudeEdits = [], SyntaxError = null, + ProjectsToRebuild = [], + ProjectsToRestart = [], }; } catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) @@ -88,6 +92,8 @@ await client.TryInvokeAsync( Diagnostics = GetInternalErrorDiagnosticData(solution, e), RudeEdits = [], SyntaxError = null, + ProjectsToRebuild = [], + ProjectsToRestart = [], }; } } @@ -133,6 +139,22 @@ await client.TryInvokeAsync( cancellationToken).ConfigureAwait(false); } + public async ValueTask UpdateBaselinesAsync(Solution solution, ImmutableArray rebuiltProjects, CancellationToken cancellationToken) + { + var client = await RemoteHostClient.TryGetClientAsync(services, cancellationToken).ConfigureAwait(false); + if (client == null) + { + GetLocalService().UpdateBaselines(sessionId, solution, rebuiltProjects); + } + else + { + var result = await client.TryInvokeAsync( + solution, + (service, solutionInfo, cancellationToken) => service.UpdateBaselinesAsync(solutionInfo, sessionId, rebuiltProjects, cancellationToken), + cancellationToken).ConfigureAwait(false); + } + } + public async ValueTask>> GetBaseActiveStatementSpansAsync(Solution solution, ImmutableArray documentIds, CancellationToken cancellationToken) { var client = await RemoteHostClient.TryGetClientAsync(services, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index 101611416f132..40607160ecfcb 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; @@ -198,21 +199,18 @@ private bool IsEmbeddedLanguageInterpolatedStringTextToken( return HasMatchingStringSyntaxAttribute(method.Parameters[0], out identifier); } - private bool IsEmbeddedLanguageStringLiteralToken( + /// + /// Checks for a string literal directly used in a location we can tell is controlled by a + /// [StringSyntax] attribute. + /// + private bool IsEmbeddedLanguageStringLiteralToken_Direct( SyntaxToken token, SemanticModel semanticModel, CancellationToken cancellationToken, - [NotNullWhen(true)] out string? identifier, - out IEnumerable? options) + [NotNullWhen(true)] out string? identifier) { identifier = null; - options = null; var syntaxFacts = Info.SyntaxFacts; - if (!syntaxFacts.IsLiteralExpression(token.Parent)) - return false; - - if (HasLanguageComment(token, syntaxFacts, out identifier, out options)) - return true; // If we're a string used in a collection initializer, treat this as a lang string if the collection itself // is properly annotated. This is for APIs that do things like DateTime.ParseExact(..., string[] formats, ...); @@ -276,6 +274,111 @@ private bool IsEmbeddedLanguageStringLiteralToken( return false; } + private bool IsEmbeddedLanguageStringLiteralToken( + SyntaxToken token, + SemanticModel semanticModel, + CancellationToken cancellationToken, + [NotNullWhen(true)] out string? identifier, + out IEnumerable? options) + { + identifier = null; + options = null; + var syntaxFacts = Info.SyntaxFacts; + if (!syntaxFacts.IsLiteralExpression(token.Parent)) + return false; + + if (HasLanguageComment(token, syntaxFacts, out identifier, out options)) + return true; + + // Check for *direct* usage of this token that indicates it's an embedded language string. Like passing it to + // an argument which has the StringSyntax attribute on it. + if (IsEmbeddedLanguageStringLiteralToken_Direct( + token, semanticModel, cancellationToken, out identifier)) + { + return true; + } + + // Now check for if the literal was assigned to a local that we then see is passed along to something that + // indicates an embedded language string at some later point. + + var container = TryFindContainer(token); + if (container is null) + return false; + + var statement = container.FirstAncestorOrSelf(syntaxFacts.IsStatement); + if (syntaxFacts.IsSimpleAssignmentStatement(statement)) + { + syntaxFacts.GetPartsOfAssignmentStatement(statement, out var left, out var right); + return container == right && + IsLocalConsumedByApiWithStringSyntaxAttribute( + semanticModel.GetSymbolInfo(left, cancellationToken).GetAnySymbol(), container, semanticModel, cancellationToken, out identifier); + } + + if (syntaxFacts.IsEqualsValueClause(container.Parent) && + syntaxFacts.IsVariableDeclarator(container.Parent.Parent)) + { + var variableDeclarator = container.Parent.Parent; + var symbol = + semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken) ?? + semanticModel.GetDeclaredSymbol(syntaxFacts.GetIdentifierOfVariableDeclarator(variableDeclarator).GetRequiredParent(), cancellationToken); + + return IsLocalConsumedByApiWithStringSyntaxAttribute(symbol, container, semanticModel, cancellationToken, out identifier); + } + + return false; + } + + private bool IsLocalConsumedByApiWithStringSyntaxAttribute( + ISymbol? symbol, + SyntaxNode tokenParent, + SemanticModel semanticModel, + CancellationToken cancellationToken, + [NotNullWhen(true)] out string? identifier) + { + identifier = null; + if (symbol is not ILocalSymbol localSymbol) + return false; + + var blockFacts = this.Info.BlockFacts; + var syntaxFacts = this.Info.SyntaxFacts; + + var block = tokenParent.AncestorsAndSelf().FirstOrDefault(blockFacts.IsExecutableBlock); + if (block is null) + return false; + + var localName = localSymbol.Name; + if (localName == "") + return false; + + // Now look at the next statements that follow for usages of this local variable. + foreach (var statement in blockFacts.GetExecutableBlockStatements(block)) + { + foreach (var descendent in statement.DescendantNodesAndSelf()) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (!syntaxFacts.IsIdentifierName(descendent)) + continue; + + var identifierToken = syntaxFacts.GetIdentifierOfIdentifierName(descendent); + if (identifierToken.ValueText != localName) + continue; + + var otherSymbol = semanticModel.GetSymbolInfo(descendent, cancellationToken).GetAnySymbol(); + + // Only do a direct check here. We don't want to continually do indirect checks where a string literal + // is assigned to one local, assigned to another local, assigned to another local, and so on. + if (localSymbol.Equals(otherSymbol) && + IsEmbeddedLanguageStringLiteralToken_Direct(identifierToken, semanticModel, cancellationToken, out identifier)) + { + return true; + } + } + } + + return false; + } + private SyntaxNode? TryFindContainer(SyntaxToken token) { var syntaxFacts = Info.SyntaxFacts; diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageInfo.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageInfo.cs index 3a820e78e81e0..41a9a81237ecc 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageInfo.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageInfo.cs @@ -11,6 +11,7 @@ namespace Microsoft.CodeAnalysis.EmbeddedLanguages; internal readonly struct EmbeddedLanguageInfo { + public readonly IBlockFacts BlockFacts; public readonly ISyntaxFacts SyntaxFacts; public readonly ISemanticFactsService SemanticFacts; public readonly IVirtualCharService VirtualCharService; @@ -18,10 +19,12 @@ internal readonly struct EmbeddedLanguageInfo public readonly ISyntaxKinds SyntaxKinds => SyntaxFacts.SyntaxKinds; public EmbeddedLanguageInfo( + IBlockFacts blockFacts, ISyntaxFacts syntaxFacts, ISemanticFactsService semanticFacts, IVirtualCharService virtualCharService) { + BlockFacts = blockFacts; SyntaxFacts = syntaxFacts; SemanticFacts = semanticFacts; VirtualCharService = virtualCharService; diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingHotReloadService.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingHotReloadService.cs index d09a0aec08776..dc3c06712f871 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingHotReloadService.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingHotReloadService.cs @@ -95,7 +95,7 @@ public async Task StartSessionAsync(Solution solution, ImmutableArray ca Contract.ThrowIfFalse(sessionId != default, "Session has not started"); var results = await _encService - .EmitSolutionUpdateAsync(sessionId, solution, s_solutionActiveStatementSpanProvider, cancellationToken) + .EmitSolutionUpdateAsync(sessionId, solution, runningProjects: [], s_solutionActiveStatementSpanProvider, cancellationToken) .ConfigureAwait(false); if (results.ModuleUpdates.Status == ModuleUpdateStatus.Ready) diff --git a/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs b/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs index 323402fe384fd..2173339e29bba 100644 --- a/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs +++ b/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs @@ -90,12 +90,24 @@ public readonly struct Updates( /// /// Running projects that need to be restarted due to rude edits in order to apply changes. /// + [Obsolete("Use ProjectIdsToRestart")] public IReadOnlySet ProjectsToRestart { get; } = projectsToRestart; /// /// Projects with changes that need to be rebuilt in order to apply changes. /// + [Obsolete("Use ProjectIdsToRebuild")] public IReadOnlySet ProjectsToRebuild { get; } = projectsToRebuild; + + /// + /// Running projects that need to be restarted due to rude edits in order to apply changes. + /// + public ImmutableArray ProjectIdsToRestart { get; } = projectsToRestart.SelectAsArray(p => p.Id); + + /// + /// Projects with changes that need to be rebuilt in order to apply changes. + /// + public ImmutableArray ProjectIdsToRebuild { get; } = projectsToRebuild.SelectAsArray(p => p.Id); } private static readonly ActiveStatementSpanProvider s_solutionActiveStatementSpanProvider = @@ -151,27 +163,32 @@ public void CapabilitiesChanged() _encService.BreakStateOrCapabilitiesChanged(GetDebuggingSession(), inBreakState: null); } + [Obsolete] public async Task<(ImmutableArray updates, ImmutableArray diagnostics)> EmitSolutionUpdateAsync(Solution solution, CancellationToken cancellationToken) { var result = await GetUpdatesAsync(solution, isRunningProject: static _ => false, cancellationToken).ConfigureAwait(false); return (result.ProjectUpdates, result.Diagnostics); } + [Obsolete] + public Task GetUpdatesAsync(Solution solution, Func isRunningProject, CancellationToken cancellationToken) + => GetUpdatesAsync(solution, solution.Projects.Where(isRunningProject).Select(static p => p.Id).ToImmutableHashSet(), cancellationToken); + /// /// Emits updates for all projects that differ between the given snapshot and the one given to the previous successful call or /// the one passed to for the first invocation. /// /// Solution snapshot. - /// Identifies projects that launched a process. + /// Identifies projects that launched a process. /// Cancellation token. /// /// Updates (one for each changed project) and Rude Edit diagnostics. Does not include syntax or semantic diagnostics. /// - public async Task GetUpdatesAsync(Solution solution, Func isRunningProject, CancellationToken cancellationToken) + public async Task GetUpdatesAsync(Solution solution, IImmutableSet runningProjects, CancellationToken cancellationToken) { var sessionId = GetDebuggingSession(); - var results = await _encService.EmitSolutionUpdateAsync(sessionId, solution, s_solutionActiveStatementSpanProvider, cancellationToken).ConfigureAwait(false); + var results = await _encService.EmitSolutionUpdateAsync(sessionId, solution, runningProjects, s_solutionActiveStatementSpanProvider, cancellationToken).ConfigureAwait(false); if (results.ModuleUpdates.Status == ModuleUpdateStatus.Ready) { @@ -180,14 +197,10 @@ public async Task GetUpdatesAsync(Solution solution, Func(); - var projectsToRebuild = new HashSet(); - results.GetProjectsToRebuildAndRestart(solution, isRunningProject, projectsToRestart, projectsToRebuild); - var projectUpdates = from update in results.ModuleUpdates.Updates let project = solution.GetRequiredProject(update.ProjectId) - where !projectsToRestart.Contains(project) + where !results.ProjectsToRestart.Contains(project.Id) select new Update( update.Module, project.Id, @@ -197,7 +210,18 @@ from update in results.ModuleUpdates.Updates update.UpdatedTypes, update.RequiredCapabilities); - return new Updates(results.ModuleUpdates.Status, diagnostics, projectUpdates.ToImmutableArray(), projectsToRestart, projectsToRebuild); + return new Updates( + results.ModuleUpdates.Status, + diagnostics, + projectUpdates.ToImmutableArray(), + results.ProjectsToRestart.Select(solution.GetRequiredProject).ToImmutableHashSet(), + results.ProjectsToRebuild.Select(solution.GetRequiredProject).ToImmutableHashSet()); + } + + public void UpdateBaselines(Solution solution, ImmutableArray projectIds) + { + var sessionId = GetDebuggingSession(); + _encService.UpdateBaselines(sessionId, solution, projectIds); } public void EndSession() diff --git a/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService_FindImplementations.cs b/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService_FindImplementations.cs index 990298e58bd3d..94210eeb81a9d 100644 --- a/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService_FindImplementations.cs +++ b/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService_FindImplementations.cs @@ -23,18 +23,17 @@ public async Task FindImplementationsAsync( IFindUsagesContext context, Document document, int position, OptionsProvider classificationOptions, CancellationToken cancellationToken) { // If this is a symbol from a metadata-as-source project, then map that symbol back to a symbol in the primary workspace. - var symbolAndProjectOpt = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync( + var symbolAndProject = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync( document, position, cancellationToken).ConfigureAwait(false); - if (symbolAndProjectOpt == null) + if (symbolAndProject is not var (symbol, project)) { await context.ReportNoResultsAsync( FeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret, cancellationToken).ConfigureAwait(false); return; } - var symbolAndProject = symbolAndProjectOpt.Value; await FindImplementationsAsync( - context, symbolAndProject.symbol, symbolAndProject.project, classificationOptions, cancellationToken).ConfigureAwait(false); + context, symbol, project, classificationOptions, cancellationToken).ConfigureAwait(false); } public static async Task FindImplementationsAsync( @@ -145,7 +144,9 @@ private static async Task> FindImplementationsWorkerAsyn var overrides = result.Where(s => s.IsOverride).ToImmutableArray(); foreach (var ov in overrides) { - for (var overridden = ov.GetOverriddenMember(); overridden != null; overridden = overridden.GetOverriddenMember()) + for (var overridden = ov.GetOverriddenMember(allowLooseMatch: true); + overridden != null; + overridden = overridden.GetOverriddenMember(allowLooseMatch: true)) { if (overridden.IsAbstract) result.Remove(overridden.OriginalDefinition); diff --git a/src/Features/Core/Portable/FindUsages/DefinitionItemFactory.cs b/src/Features/Core/Portable/FindUsages/DefinitionItemFactory.cs index dbe6714f0003f..c50d0c0763b54 100644 --- a/src/Features/Core/Portable/FindUsages/DefinitionItemFactory.cs +++ b/src/Features/Core/Portable/FindUsages/DefinitionItemFactory.cs @@ -222,9 +222,9 @@ internal static ImmutableArray GetMetadataLocations(ISymbol de private static ImmutableArray GetSourceLocations(ISymbol definition, ImmutableArray locations, Solution solution, bool includeHiddenLocations) { - // Assembly, module and global namespace locations include all source documents; displaying all of them is not useful. + // Assembly, module, global namespace and preprocessing symbol locations include all source documents; displaying all of them is not useful. // We could consider creating a definition item that points to the project source instead. - if (definition is IAssemblySymbol or IModuleSymbol or INamespaceSymbol { IsGlobalNamespace: true }) + if (definition is IAssemblySymbol or IModuleSymbol or INamespaceSymbol { IsGlobalNamespace: true } or IPreprocessingSymbol) { return []; } diff --git a/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs b/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs index 6a01b6efe873a..9e9b542e57027 100644 --- a/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs +++ b/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs @@ -259,7 +259,14 @@ internal readonly struct SerializableClassifiedSpansAndHighlightSpan( public readonly TextSpan HighlightSpan = highlightSpan; public static SerializableClassifiedSpansAndHighlightSpan Dehydrate(ClassifiedSpansAndHighlightSpan classifiedSpansAndHighlightSpan) - => new(SerializableClassifiedSpans.Dehydrate(classifiedSpansAndHighlightSpan.ClassifiedSpans), classifiedSpansAndHighlightSpan.HighlightSpan); + { + using var _ = Classifier.GetPooledList(out var temp); + + foreach (var span in classifiedSpansAndHighlightSpan.ClassifiedSpans) + temp.Add(span); + + return new(SerializableClassifiedSpans.Dehydrate(temp), classifiedSpansAndHighlightSpan.HighlightSpan); + } public ClassifiedSpansAndHighlightSpan Rehydrate() => new(this.ClassifiedSpans.Rehydrate(), this.HighlightSpan); diff --git a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.CodeAction.cs b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.CodeAction.cs index 6c3ca86fd1c93..e7872f4dae9d6 100644 --- a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.CodeAction.cs +++ b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.CodeAction.cs @@ -140,8 +140,7 @@ private bool GetPredefinedTypeKindOption(State state, out TypeKindOptions typeKi return true; } - if (_service.TryGetBaseList(state.NameOrMemberAccessExpression, out var typeKindValue) || - _service.TryGetBaseList(state.SimpleName, out typeKindValue)) + if (_service.TryGetBaseList(state.NameOrMemberAccessExpression, out var typeKindValue)) { typeKindValueFinal = typeKindValue; return true; diff --git a/src/Features/Core/Portable/GoToBase/AbstractGoToBaseService.cs b/src/Features/Core/Portable/GoToBase/AbstractGoToBaseService.cs index 028f320ab63c0..734402b53aad1 100644 --- a/src/Features/Core/Portable/GoToBase/AbstractGoToBaseService.cs +++ b/src/Features/Core/Portable/GoToBase/AbstractGoToBaseService.cs @@ -30,18 +30,16 @@ internal abstract class AbstractGoToBaseService : IGoToBaseService public async Task FindBasesAsync(IFindUsagesContext context, Document document, int position, OptionsProvider classificationOptions, CancellationToken cancellationToken) { - var symbolAndProjectOpt = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync( + var symbolAndProject = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync( document, position, cancellationToken).ConfigureAwait(false); - if (symbolAndProjectOpt == null) + if (symbolAndProject is not var (symbol, project)) { await context.ReportNoResultsAsync( FeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret, cancellationToken).ConfigureAwait(false); return; } - var (symbol, project) = symbolAndProjectOpt.Value; - var solution = project.Solution; var bases = FindBaseHelpers.FindBases(symbol, solution, cancellationToken); if (bases.Length == 0 && symbol is IMethodSymbol { MethodKind: MethodKind.Constructor } constructor) diff --git a/src/Features/Core/Portable/InheritanceMargin/AbstractInheritanceMarginService_Helpers.cs b/src/Features/Core/Portable/InheritanceMargin/AbstractInheritanceMarginService_Helpers.cs index e92451625a619..5257ed9709237 100644 --- a/src/Features/Core/Portable/InheritanceMargin/AbstractInheritanceMarginService_Helpers.cs +++ b/src/Features/Core/Portable/InheritanceMargin/AbstractInheritanceMarginService_Helpers.cs @@ -371,7 +371,7 @@ private static async ValueTask AddInheritanceMemberItemsForMembersAsync( var allOverridingSymbols = await SymbolFinder.FindOverridesArrayAsync(memberSymbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false); // Go up the inheritance chain to find all overridden targets - var overriddenSymbols = GetOverriddenSymbols(memberSymbol); + var overriddenSymbols = GetOverriddenSymbols(memberSymbol, allowLooseMatch: true); // Go up the inheritance chain to find all the implemented targets. var implementedSymbols = GetImplementedSymbolsForTypeMember(memberSymbol, overriddenSymbols); @@ -650,24 +650,20 @@ private static async Task> GetImplementingSymbolsForType /// /// Get overridden members the . /// - private static ImmutableArray GetOverriddenSymbols(ISymbol memberSymbol) + private static ImmutableArray GetOverriddenSymbols(ISymbol memberSymbol, bool allowLooseMatch) { if (memberSymbol is INamedTypeSymbol) - { return []; - } - else - { - using var _ = ArrayBuilder.GetInstance(out var builder); - for (var overriddenMember = memberSymbol.GetOverriddenMember(); - overriddenMember != null; - overriddenMember = overriddenMember.GetOverriddenMember()) - { - builder.Add(overriddenMember.OriginalDefinition); - } - return builder.ToImmutableArray(); + using var _ = ArrayBuilder.GetInstance(out var builder); + for (var overriddenMember = memberSymbol.GetOverriddenMember(allowLooseMatch); + overriddenMember != null; + overriddenMember = overriddenMember.GetOverriddenMember(allowLooseMatch)) + { + builder.Add(overriddenMember.OriginalDefinition); } + + return builder.ToImmutableAndClear(); } /// diff --git a/src/Features/Core/Portable/InheritanceMargin/InheritanceMarginItem.cs b/src/Features/Core/Portable/InheritanceMargin/InheritanceMarginItem.cs index 4e17a45b3c04e..5cfa259e41311 100644 --- a/src/Features/Core/Portable/InheritanceMargin/InheritanceMarginItem.cs +++ b/src/Features/Core/Portable/InheritanceMargin/InheritanceMarginItem.cs @@ -55,6 +55,9 @@ public override int GetHashCode() public override bool Equals(object? obj) => obj is InheritanceMarginItem item && Equals(item); + public override string ToString() + => string.Join("", DisplayTexts.Select(d => d.Text)); + public bool Equals(InheritanceMarginItem other) => this.LineNumber == other.LineNumber && this.TopLevelDisplayText == other.TopLevelDisplayText && diff --git a/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/StructuralTypeDisplayInfo.cs b/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/StructuralTypeDisplayInfo.cs index 041e3ac58e745..813d2eff75a5c 100644 --- a/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/StructuralTypeDisplayInfo.cs +++ b/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/StructuralTypeDisplayInfo.cs @@ -53,7 +53,7 @@ public static bool ReplaceStructuralTypes( if (structuralTypeToName.TryGetValue(type, out var name) && part.ToString() != name) { - newParts.Add(new SymbolDisplayPart(part.Kind, part.Symbol, name)); + newParts.Add(new SymbolDisplayPart(part.Kind, symbol: null, name)); changed = true; continue; } diff --git a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs index f55d35d7ec1df..0d409a29b664b 100644 --- a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs +++ b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.DocumentationComments; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; @@ -146,6 +147,9 @@ protected SemanticModel GetSemanticModel(SyntaxTree tree) protected Compilation Compilation => _semanticModel.Compilation; + protected virtual ImmutableArray WrapConstraints(ISymbol symbol, ImmutableArray displayParts) + => displayParts; + private async Task AddPartsAsync(ImmutableArray symbols) { var firstSymbol = symbols[0]; @@ -458,13 +462,32 @@ private void AddDescriptionForNamedType(INamedTypeSymbol symbol) AddAwaitablePrefix(); } - AddSymbolDescription(symbol); + if (symbol.TypeKind == TypeKind.Delegate) + { + var style = s_descriptionStyle.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + // Under the covers anonymous delegates are represented with generic types. However, we don't want + // to see the unbound form of that generic. We want to see the fully instantiated signature. + var displayParts = symbol.IsAnonymousDelegateType() + ? symbol.ToDisplayParts(style) + : symbol.OriginalDefinition.ToDisplayParts(style); + + AddToGroup(SymbolDescriptionGroups.MainDescription, WrapConstraints(symbol, displayParts)); + } + else + { + AddToGroup(SymbolDescriptionGroups.MainDescription, + WrapConstraints(symbol.OriginalDefinition, symbol.OriginalDefinition.ToDisplayParts(s_descriptionStyle))); + } + + if (symbol.NullableAnnotation == NullableAnnotation.Annotated) + AddToGroup(SymbolDescriptionGroups.MainDescription, new SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, null, "?")); if (!symbol.IsUnboundGenericType && !TypeArgumentsAndParametersAreSame(symbol) && !symbol.IsAnonymousDelegateType()) { - var allTypeParameters = symbol.GetAllTypeParameters().ToList(); + var allTypeParameters = symbol.GetAllTypeParameters(); var allTypeArguments = symbol.GetAllTypeArguments().ToList(); AddTypeParameterMapPart(allTypeParameters, allTypeArguments); @@ -478,28 +501,6 @@ private void AddDescriptionForNamedType(INamedTypeSymbol symbol) } } - private void AddSymbolDescription(INamedTypeSymbol symbol) - { - if (symbol.TypeKind == TypeKind.Delegate) - { - var style = s_descriptionStyle.WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes); - - // Under the covers anonymous delegates are represented with generic types. However, we don't want - // to see the unbound form of that generic. We want to see the fully instantiated signature. - AddToGroup(SymbolDescriptionGroups.MainDescription, symbol.IsAnonymousDelegateType() - ? symbol.ToDisplayParts(style) - : symbol.OriginalDefinition.ToDisplayParts(style)); - } - else - { - AddToGroup(SymbolDescriptionGroups.MainDescription, - symbol.OriginalDefinition.ToDisplayParts(s_descriptionStyle)); - } - - if (symbol.NullableAnnotation == NullableAnnotation.Annotated) - AddToGroup(SymbolDescriptionGroups.MainDescription, new SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, null, "?")); - } - private static bool TypeArgumentsAndParametersAreSame(INamedTypeSymbol symbol) { var typeArguments = symbol.GetAllTypeArguments().ToList(); @@ -725,12 +726,12 @@ private static int GetOverloadCount(ImmutableArray symbolGroup) } protected void AddTypeParameterMapPart( - List typeParameters, + ImmutableArray typeParameters, List typeArguments) { - var parts = new List(); + using var _ = ArrayBuilder.GetInstance(out var parts); - var count = typeParameters.Count; + var count = typeParameters.Length; for (var i = 0; i < count; i++) { parts.AddRange(TypeParameterName(typeParameters[i].Name)); @@ -746,8 +747,7 @@ protected void AddTypeParameterMapPart( } } - AddToGroup(SymbolDescriptionGroups.TypeParameterMap, - parts); + AddToGroup(SymbolDescriptionGroups.TypeParameterMap, parts); } protected void AddToGroup(SymbolDescriptionGroups group, params SymbolDisplayPart[] partsArray) diff --git a/src/Features/Core/Portable/Navigation/AbstractNavigableItemsService.cs b/src/Features/Core/Portable/Navigation/AbstractNavigableItemsService.cs index 11c66bdf46e6b..9e373be3ee6ec 100644 --- a/src/Features/Core/Portable/Navigation/AbstractNavigableItemsService.cs +++ b/src/Features/Core/Portable/Navigation/AbstractNavigableItemsService.cs @@ -14,8 +14,14 @@ namespace Microsoft.CodeAnalysis.Navigation; internal abstract class AbstractNavigableItemsService : INavigableItemsService { - public async Task> GetNavigableItemsAsync( + public Task> GetNavigableItemsAsync( Document document, int position, CancellationToken cancellationToken) + { + return GetNavigableItemsAsync(document, position, forSymbolType: false, cancellationToken); + } + + public async Task> GetNavigableItemsAsync( + Document document, int position, bool forSymbolType, CancellationToken cancellationToken) { var symbolService = document.GetRequiredLanguageService(); @@ -46,6 +52,22 @@ await GetSymbolAsync(document.WithFrozenPartialSemantics(cancellationToken)).Con if (symbol is null or IErrorTypeSymbol) return null; + if (forSymbolType) + { + // We have found the symbol at the position in the document. Now we need to find the symbol's type. + var typeSymbol = symbol.GetSymbolType() as ISymbol; + if (typeSymbol is null) + return null; + + typeSymbol = await SymbolFinder.FindSourceDefinitionAsync(typeSymbol, solution, cancellationToken).ConfigureAwait(false) ?? typeSymbol; + typeSymbol = await GoToDefinitionFeatureHelpers.TryGetPreferredSymbolAsync(solution, typeSymbol, cancellationToken).ConfigureAwait(false); + + if (typeSymbol is null or IErrorTypeSymbol) + return null; + + symbol = typeSymbol; + } + return (symbol, solution); } } diff --git a/src/Features/Core/Portable/Navigation/INavigableItemsService.cs b/src/Features/Core/Portable/Navigation/INavigableItemsService.cs index 6fef80259e280..d792ee79e48ec 100644 --- a/src/Features/Core/Portable/Navigation/INavigableItemsService.cs +++ b/src/Features/Core/Portable/Navigation/INavigableItemsService.cs @@ -10,10 +10,10 @@ namespace Microsoft.CodeAnalysis.Navigation; /// -/// Service used for features that want to find all the locations to potentially navigate to for a symbol at a -/// particular location, with enough information provided to display those locations in a rich fashion. Differs from -/// in that this can show a rich display of the items, not just navigate to -/// them. +/// Service used for features that want to find all the locations to potentially navigate to for a symbol or its type +/// at a particular location, with enough information provided to display those locations in a rich fashion. Differs +/// from in that this can show a rich display of the items, not just navigate +/// to them. /// internal interface INavigableItemsService : ILanguageService { @@ -21,4 +21,9 @@ internal interface INavigableItemsService : ILanguageService /// Finds the definitions for the symbol at the specific position in the document. /// Task> GetNavigableItemsAsync(Document document, int position, CancellationToken cancellationToken); + + /// + /// Finds the definitions for the symbol or its type at the specific position in the document. + /// + Task> GetNavigableItemsAsync(Document document, int position, bool forSymbolType, CancellationToken cancellationToken); } diff --git a/src/Features/Core/Portable/RelatedDocuments/AbstractRelatedDocumentsService.cs b/src/Features/Core/Portable/RelatedDocuments/AbstractRelatedDocumentsService.cs index 42a8920a612fd..3e103bc466b37 100644 --- a/src/Features/Core/Portable/RelatedDocuments/AbstractRelatedDocumentsService.cs +++ b/src/Features/Core/Portable/RelatedDocuments/AbstractRelatedDocumentsService.cs @@ -135,7 +135,7 @@ void ProduceItems( foreach (var syntaxReference in symbol.DeclaringSyntaxReferences) { var documentId = solution.GetDocument(syntaxReference.SyntaxTree)?.Id; - if (documentId != null && seenDocumentIds.Add(documentId)) + if (documentId != null && !documentId.IsSourceGenerated && seenDocumentIds.Add(documentId)) callback(documentId); } } diff --git a/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs b/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs index 81855b502311f..0b78a77b904cb 100644 --- a/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs +++ b/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs @@ -147,6 +147,9 @@ MethodKind.Conversion or break; + case SymbolKind.Preprocessing: + return Glyph.Keyword; + case SymbolKind.RangeVariable: return Glyph.RangeVariable; diff --git a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs index 77ef502baac75..77bc02712d424 100644 --- a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs +++ b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs @@ -24,7 +24,9 @@ namespace Microsoft.CodeAnalysis.UseAutoProperty; using static UseAutoPropertiesHelpers; -internal abstract class AbstractUseAutoPropertyCodeFixProvider : CodeFixProvider +internal abstract partial class AbstractUseAutoPropertyCodeFixProvider + : CodeFixProvider + where TProvider : AbstractUseAutoPropertyCodeFixProvider where TTypeDeclarationSyntax : SyntaxNode where TPropertyDeclaration : SyntaxNode where TVariableDeclarator : SyntaxNode @@ -36,7 +38,8 @@ internal abstract class AbstractUseAutoPropertyCodeFixProvider FixableDiagnosticIds => [IDEDiagnosticIds.UseAutoPropertyDiagnosticId]; - public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + public sealed override FixAllProvider GetFixAllProvider() + => new UseAutoPropertyFixAllProvider((TProvider)this); protected abstract TPropertyDeclaration GetPropertyDeclaration(SyntaxNode node); protected abstract SyntaxNode GetNodeToRemove(TVariableDeclarator declarator); @@ -60,6 +63,8 @@ protected abstract Task UpdatePropertyAsync( public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) { + var solution = context.Document.Project.Solution; + foreach (var diagnostic in context.Diagnostics) { var priority = diagnostic.Severity == DiagnosticSeverity.Hidden @@ -68,7 +73,7 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) context.RegisterCodeFix(CodeAction.SolutionChangeAction.Create( AnalyzersResources.Use_auto_property, - cancellationToken => ProcessResultAsync(context, diagnostic, cancellationToken), + cancellationToken => ProcessResultAsync(solution, solution, diagnostic, cancellationToken), equivalenceKey: nameof(AnalyzersResources.Use_auto_property), priority), diagnostic); @@ -77,23 +82,19 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) return Task.CompletedTask; } - private async Task ProcessResultAsync(CodeFixContext context, Diagnostic diagnostic, CancellationToken cancellationToken) + private async Task ProcessResultAsync( + Solution originalSolution, Solution currentSolution, Diagnostic diagnostic, CancellationToken cancellationToken) { - var locations = diagnostic.AdditionalLocations; + var (field, property) = await MapDiagnosticToCurrentSolutionAsync( + diagnostic, originalSolution, currentSolution, cancellationToken).ConfigureAwait(false); - var propertyLocation = locations[0]; - var declaratorLocation = locations[1]; + if (field == null || property == null) + return currentSolution; - var solution = context.Document.Project.Solution; - var declarator = (TVariableDeclarator)declaratorLocation.FindNode(cancellationToken); - var fieldDocument = solution.GetRequiredDocument(declarator.SyntaxTree); - var fieldSemanticModel = await fieldDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var fieldSymbol = (IFieldSymbol)fieldSemanticModel.GetRequiredDeclaredSymbol(declarator, cancellationToken); + var locations = diagnostic.AdditionalLocations; - var property = GetPropertyDeclaration(propertyLocation.FindNode(cancellationToken)); - var propertyDocument = solution.GetRequiredDocument(property.SyntaxTree); - var propertySemanticModel = await propertyDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var propertySymbol = (IPropertySymbol)propertySemanticModel.GetRequiredDeclaredSymbol(property, cancellationToken); + var fieldDocument = currentSolution.GetRequiredDocument(field.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken).SyntaxTree); + var propertyDocument = currentSolution.GetRequiredDocument(property.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken).SyntaxTree); var isTrivialGetAccessor = diagnostic.Properties.ContainsKey(IsTrivialGetAccessor); var isTrivialSetAccessor = diagnostic.Properties.ContainsKey(IsTrivialSetAccessor); @@ -105,24 +106,27 @@ private async Task ProcessResultAsync(CodeFixContext context, Diagnost var renameOptions = new SymbolRenameOptions(); var fieldLocations = await Renamer.FindRenameLocationsAsync( - solution, fieldSymbol, renameOptions, cancellationToken).ConfigureAwait(false); + currentSolution, field, renameOptions, cancellationToken).ConfigureAwait(false); + + var declarator = (TVariableDeclarator)field.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken); + var propertyDeclaration = GetPropertyDeclaration(property.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken)); // First, create the updated property we want to replace the old property with var isWrittenToOutsideOfConstructor = IsWrittenToOutsideOfConstructorOrProperty( - fieldSymbol, fieldLocations, property, cancellationToken); + field, fieldLocations, propertyDeclaration, cancellationToken); if (!isTrivialGetAccessor || - (propertySymbol.SetMethod != null && !isTrivialSetAccessor)) + (property.SetMethod != null && !isTrivialSetAccessor)) { // We have at least a non-trivial getter/setter. Those will not be rewritten to `get;/set;`. As such, we // need to update the property to reference `field` or itself instead of the actual field. - property = RewriteFieldReferencesInProperty(property, fieldLocations, cancellationToken); + propertyDeclaration = RewriteFieldReferencesInProperty(propertyDeclaration, fieldLocations, cancellationToken); } var updatedProperty = await UpdatePropertyAsync( propertyDocument, compilation, - fieldSymbol, propertySymbol, - declarator, property, + field, property, + declarator, propertyDeclaration, isWrittenToOutsideOfConstructor, isTrivialGetAccessor, isTrivialSetAccessor, cancellationToken).ConfigureAwait(false); @@ -161,31 +165,31 @@ private async Task ProcessResultAsync(CodeFixContext context, Diagnost var filteredLocations = fieldLocations.Filter( (documentId, span) => - fieldDocument.Id == documentId ? !span.IntersectsWith(declaratorLocation.SourceSpan) : true && // The span check only makes sense if we are in the same file - CanEditDocument(solution, documentId, linkedFiles, canEdit)); + fieldDocument.Id == documentId ? !span.IntersectsWith(declarator.Span) : true && // The span check only makes sense if we are in the same file + CanEditDocument(currentSolution, documentId, linkedFiles, canEdit)); var resolution = await filteredLocations.ResolveConflictsAsync( - fieldSymbol, propertySymbol.Name, - nonConflictSymbolKeys: [propertySymbol.GetSymbolKey(cancellationToken)], + field, property.Name, + nonConflictSymbolKeys: [property.GetSymbolKey(cancellationToken)], cancellationToken).ConfigureAwait(false); Contract.ThrowIfFalse(resolution.IsSuccessful); - solution = resolution.NewSolution; + currentSolution = resolution.NewSolution; // Now find the field and property again post rename. - fieldDocument = solution.GetRequiredDocument(fieldDocument.Id); - propertyDocument = solution.GetRequiredDocument(propertyDocument.Id); + fieldDocument = currentSolution.GetRequiredDocument(fieldDocument.Id); + propertyDocument = currentSolution.GetRequiredDocument(propertyDocument.Id); Debug.Assert(fieldDocument.Project == propertyDocument.Project); compilation = await fieldDocument.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); - fieldSymbol = (IFieldSymbol?)fieldSymbol.GetSymbolKey(cancellationToken).Resolve(compilation, cancellationToken: cancellationToken).Symbol; - propertySymbol = (IPropertySymbol?)propertySymbol.GetSymbolKey(cancellationToken).Resolve(compilation, cancellationToken: cancellationToken).Symbol; - Contract.ThrowIfTrue(fieldSymbol == null || propertySymbol == null); + field = (IFieldSymbol?)field.GetSymbolKey(cancellationToken).Resolve(compilation, cancellationToken: cancellationToken).Symbol; + property = (IPropertySymbol?)property.GetSymbolKey(cancellationToken).Resolve(compilation, cancellationToken: cancellationToken).Symbol; + Contract.ThrowIfTrue(field == null || property == null); - declarator = (TVariableDeclarator)await fieldSymbol.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false); - property = GetPropertyDeclaration(await propertySymbol.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false)); + declarator = (TVariableDeclarator)field.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken); + propertyDeclaration = GetPropertyDeclaration(property.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken)); var nodeToRemove = GetNodeToRemove(declarator); @@ -222,7 +226,7 @@ private async Task ProcessResultAsync(CodeFixContext context, Diagnost { var syntaxFacts = fieldDocument.GetRequiredLanguageService(); var bannerService = fieldDocument.GetRequiredLanguageService(); - if (WillRemoveFirstFieldInTypeDirectlyAboveProperty(syntaxFacts, property, nodeToRemove) && + if (WillRemoveFirstFieldInTypeDirectlyAboveProperty(syntaxFacts, propertyDeclaration, nodeToRemove) && bannerService.GetLeadingBlankLines(nodeToRemove).Length == 0) { updatedProperty = bannerService.GetNodeWithoutLeadingBlankLines(updatedProperty); @@ -236,13 +240,13 @@ private async Task ProcessResultAsync(CodeFixContext context, Diagnost var declaratorTreeRoot = await fieldDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var editor = new SyntaxEditor(declaratorTreeRoot, fieldDocument.Project.Solution.Services); - editor.ReplaceNode(property, updatedProperty); + editor.ReplaceNode(propertyDeclaration, updatedProperty); editor.RemoveNode(nodeToRemove, syntaxRemoveOptions); var newRoot = editor.GetChangedRoot(); newRoot = await FormatAsync(newRoot, fieldDocument, updatedProperty, cancellationToken).ConfigureAwait(false); - return solution.WithDocumentSyntaxRoot(fieldDocument.Id, newRoot); + return currentSolution.WithDocumentSyntaxRoot(fieldDocument.Id, newRoot); } else { @@ -252,18 +256,56 @@ private async Task ProcessResultAsync(CodeFixContext context, Diagnost var newFieldTreeRoot = fieldTreeRoot.RemoveNode(nodeToRemove, syntaxRemoveOptions); Contract.ThrowIfNull(newFieldTreeRoot); - var newPropertyTreeRoot = propertyTreeRoot.ReplaceNode(property, updatedProperty); + var newPropertyTreeRoot = propertyTreeRoot.ReplaceNode(propertyDeclaration, updatedProperty); newFieldTreeRoot = await FormatAsync(newFieldTreeRoot, fieldDocument, updatedProperty, cancellationToken).ConfigureAwait(false); newPropertyTreeRoot = await FormatAsync(newPropertyTreeRoot, propertyDocument, updatedProperty, cancellationToken).ConfigureAwait(false); - var updatedSolution = solution.WithDocumentSyntaxRoot(fieldDocument.Id, newFieldTreeRoot); + var updatedSolution = currentSolution.WithDocumentSyntaxRoot(fieldDocument.Id, newFieldTreeRoot); updatedSolution = updatedSolution.WithDocumentSyntaxRoot(propertyDocument.Id, newPropertyTreeRoot); return updatedSolution; } } + private async Task<(IFieldSymbol? fieldSymbol, IPropertySymbol? propertySymbol)> MapDiagnosticToCurrentSolutionAsync( + Diagnostic diagnostic, + Solution originalSolution, + Solution currentSolution, + CancellationToken cancellationToken) + { + var locations = diagnostic.AdditionalLocations; + + var propertyLocation = locations[0]; + var declaratorLocation = locations[1]; + + // Look up everything in the original solution. + + var declarator = (TVariableDeclarator)declaratorLocation.FindNode(cancellationToken); + var fieldDocument = originalSolution.GetRequiredDocument(declarator.SyntaxTree); + var fieldSemanticModel = await fieldDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var fieldSymbol = (IFieldSymbol)fieldSemanticModel.GetRequiredDeclaredSymbol(declarator, cancellationToken); + + var property = GetPropertyDeclaration(propertyLocation.FindNode(cancellationToken)); + var propertyDocument = originalSolution.GetRequiredDocument(property.SyntaxTree); + var propertySemanticModel = await propertyDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var propertySymbol = (IPropertySymbol)propertySemanticModel.GetRequiredDeclaredSymbol(property, cancellationToken); + + Contract.ThrowIfFalse(fieldDocument.Project == propertyDocument.Project); + + // If we're just starting, no need to map anything. + if (originalSolution != currentSolution) + { + var currentProject = currentSolution.GetRequiredProject(fieldDocument.Project.Id); + var currentCompilation = await currentProject.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); + + fieldSymbol = fieldSymbol.GetSymbolKey(cancellationToken).Resolve(currentCompilation, cancellationToken: cancellationToken).GetAnySymbol() as IFieldSymbol; + propertySymbol = propertySymbol.GetSymbolKey(cancellationToken).Resolve(currentCompilation, cancellationToken: cancellationToken).GetAnySymbol() as IPropertySymbol; + } + + return (fieldSymbol, propertySymbol); + } + private static SyntaxRemoveOptions CreateSyntaxRemoveOptions(SyntaxNode nodeToRemove) { var syntaxRemoveOptions = SyntaxGenerator.DefaultRemoveOptions; diff --git a/src/Features/Core/Portable/UseAutoProperty/UseAutoPropertyFixAllProvider.cs b/src/Features/Core/Portable/UseAutoProperty/UseAutoPropertyFixAllProvider.cs new file mode 100644 index 0000000000000..333052f575da3 --- /dev/null +++ b/src/Features/Core/Portable/UseAutoProperty/UseAutoPropertyFixAllProvider.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.UseAutoProperty; + +internal abstract partial class AbstractUseAutoPropertyCodeFixProvider< + TProvider, + TTypeDeclarationSyntax, + TPropertyDeclaration, + TVariableDeclarator, + TConstructorDeclaration, + TExpression> +{ + private sealed class UseAutoPropertyFixAllProvider(TProvider provider) : FixAllProvider + { + public override Task GetFixAsync(FixAllContext fixAllContext) + => DefaultFixAllProviderHelpers.GetFixAsync( + fixAllContext.GetDefaultFixAllTitle(), fixAllContext, FixAllContextsHelperAsync); + + private async Task FixAllContextsHelperAsync(FixAllContext originalContext, ImmutableArray contexts) + { + var cancellationToken = originalContext.CancellationToken; + + // Very slow approach, but the only way we know how to do this correctly and without colliding edits. We + // effectively apply each fix one at a time, moving the solution forward each time. As we process each + // diagnostic, we attempt to re-recover the field/property it was referring to in the original solution to + // the current solution. + var originalSolution = originalContext.Solution; + var currentSolution = originalSolution; + + foreach (var currentContext in contexts) + { + var documentToDiagnostics = await FixAllContextHelper.GetDocumentDiagnosticsToFixAsync(currentContext).ConfigureAwait(false); + foreach (var (_, diagnostics) in documentToDiagnostics) + { + foreach (var diagnostic in diagnostics) + { + currentSolution = await provider.ProcessResultAsync( + originalSolution, currentSolution, diagnostic, cancellationToken).ConfigureAwait(false); + } + } + } + + return currentSolution; + } + } +} diff --git a/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs b/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs index 06dd59037896e..2585055daa8dc 100644 --- a/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs +++ b/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs @@ -51,6 +51,7 @@ static Test() public Test() { _sharedState = new SharedVerifierState(this, DefaultFileExt); + this.FixedState.InheritanceMode = StateInheritanceMode.AutoInherit; } /// diff --git a/src/Features/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentsSnippetService.cs b/src/Features/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentsSnippetService.cs index 33c5570262629..7dacd039767e7 100644 --- a/src/Features/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentsSnippetService.cs +++ b/src/Features/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentsSnippetService.cs @@ -12,6 +12,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.DocumentationComments { internal static class OmniSharpDocumentationCommentsSnippetService { +#pragma warning disable IDE0060 // Remove unused parameter public static OmniSharpDocumentationCommentSnippet? GetDocumentationCommentSnippetOnCharacterTyped( Document document, SyntaxTree syntaxTree, @@ -21,7 +22,7 @@ internal static class OmniSharpDocumentationCommentsSnippetService CancellationToken cancellationToken) { var service = document.GetRequiredLanguageService(); - return Translate(service.GetDocumentationCommentSnippetOnCharacterTyped(syntaxTree, text, position, options.UnderlyingObject, cancellationToken)); + return Translate(service.GetDocumentationCommentSnippetOnCharacterTyped(ParsedDocument.CreateSynchronously(document, cancellationToken), position, options.UnderlyingObject, cancellationToken)); } public static OmniSharpDocumentationCommentSnippet? GetDocumentationCommentSnippetOnEnterTyped( @@ -33,7 +34,7 @@ internal static class OmniSharpDocumentationCommentsSnippetService CancellationToken cancellationToken) { var service = document.GetRequiredLanguageService(); - return Translate(service.GetDocumentationCommentSnippetOnEnterTyped(syntaxTree, text, position, options.UnderlyingObject, cancellationToken)); + return Translate(service.GetDocumentationCommentSnippetOnEnterTyped(ParsedDocument.CreateSynchronously(document, cancellationToken), position, options.UnderlyingObject, cancellationToken)); } private static OmniSharpDocumentationCommentSnippet? Translate(DocumentationCommentSnippet? result) diff --git a/src/Features/RulesMissingDocumentation.md b/src/Features/RulesMissingDocumentation.md index ad42d72a548d3..0e66ef5c49bf1 100644 --- a/src/Features/RulesMissingDocumentation.md +++ b/src/Features/RulesMissingDocumentation.md @@ -11,8 +11,10 @@ IDE0302 | | Simplify collection initialization | IDE0304 | | Simplify collection initialization | IDE0305 | | Simplify collection initialization | +IDE0306 | | Simplify collection initialization | IDE0320 | | Make anonymous function static | IDE0330 | | Use 'System.Threading.Lock' | +IDE0340 | | Use unbound generic type | IDE1007 | | | IDE2000 | | Avoid multiple blank lines | IDE2001 | | Embedded statements must be on their own line | diff --git a/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index c185b9d7660eb..f5928559c4d59 100644 --- a/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -537,19 +537,19 @@ public async Task ErrorReadingModuleFile(bool breakMode) if (breakMode) { - AssertEx.Equal( + AssertEx.SequenceEqual( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=3", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges={6A6F7270-0000-4000-8000-000000000000}", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges={6A6F7270-0000-4000-8000-000000000000}|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_EmitDeltaErrorId: SessionId=1|EditSessionId=2|ErrorId=ENC1001" ], _telemetryLog); } else { - AssertEx.Equal( + AssertEx.SequenceEqual( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=1|EmptyHotReloadSessionCount=1", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges={6A6F7270-0000-4000-8000-000000000000}", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges={6A6F7270-0000-4000-8000-000000000000}|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_EmitDeltaErrorId: SessionId=1|EditSessionId=2|ErrorId=ENC1001" ], _telemetryLog); } @@ -659,7 +659,7 @@ public async Task ErrorReadingSourceFile() AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=1", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=" + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=" ], _telemetryLog); } @@ -717,7 +717,7 @@ public async Task FileAdded(bool breakMode) AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=2", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=" + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=" ], _telemetryLog); } else @@ -725,7 +725,7 @@ public async Task FileAdded(bool breakMode) AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=1|EmptyHotReloadSessionCount=0", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=" + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=" ], _telemetryLog); } } @@ -893,7 +893,7 @@ void M() AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=1", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_EmitDeltaErrorId: SessionId=1|EditSessionId=2|ErrorId=ENC2016" ], _telemetryLog); } @@ -1002,7 +1002,7 @@ public async Task RudeEdits(bool breakMode) AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=2", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=110|RudeEditSyntaxKind=8910|RudeEditBlocking=True|RudeEditProjectId={6A6F7270-0000-4000-8000-000000000000}" ], _telemetryLog); } @@ -1011,7 +1011,7 @@ public async Task RudeEdits(bool breakMode) AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=1|EmptyHotReloadSessionCount=0", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=110|RudeEditSyntaxKind=8910|RudeEditBlocking=True|RudeEditProjectId={6A6F7270-0000-4000-8000-000000000000}" ], _telemetryLog); } @@ -1214,7 +1214,7 @@ public async Task RudeEdits_DocumentOutOfSync(bool breakMode) AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=2", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=110|RudeEditSyntaxKind=8875|RudeEditBlocking=True|RudeEditProjectId={6A6F7270-0000-4000-8000-000000000000}" ], _telemetryLog); } @@ -1223,7 +1223,7 @@ public async Task RudeEdits_DocumentOutOfSync(bool breakMode) AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=1|EmptyHotReloadSessionCount=0", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=110|RudeEditSyntaxKind=8875|RudeEditBlocking=True|RudeEditProjectId={6A6F7270-0000-4000-8000-000000000000}" ], _telemetryLog); } @@ -1328,6 +1328,117 @@ public async Task RudeEdits_DelayLoadedModule() EndDebuggingSession(debuggingSession); } + [Theory] + [CombinatorialData] + public async Task RudeEdits_UpdateBaseline(bool validChangeBeforeRudeEdit) + { + var source1 = "abstract class C { }"; + var source2 = "abstract class C { void F() {} }"; + var source3 = "abstract class C { void F() {} public abstract void G(); }"; + var source4 = "abstract class C { void F() {} public abstract void G(); void H() {} }"; + + var dir = Temp.CreateDirectory(); + var sourceFile = dir.CreateFile("a.cs").WriteAllText(source1, Encoding.UTF8); + + using var _ = CreateWorkspace(out var solution, out var service); + (solution, var document) = AddDefaultTestProject(solution, source1); + + var documentId = document.Id; + var projectId = document.Project.Id; + var moduleId = EmitAndLoadLibraryToDebuggee(source1, sourceFilePath: sourceFile.Path); + + var debuggingSession = await StartDebuggingSessionAsync(service, solution); + + EmitSolutionUpdateResults result; + var readers = ImmutableArray.Empty; + + // change the source (valid edit): + if (validChangeBeforeRudeEdit) + { + solution = solution.WithDocumentText(documentId, CreateText(source2)); + + result = await debuggingSession.EmitSolutionUpdateAsync(solution, runningProjects: [projectId], s_noActiveSpans, CancellationToken.None); + Assert.Equal(ModuleUpdateStatus.Ready, result.ModuleUpdates.Status); + Assert.Empty(result.ProjectsToRebuild); + Assert.Empty(result.ProjectsToRestart); + + // baseline should be present: + readers = debuggingSession.GetTestAccessor().GetBaselineModuleReaders(); + Assert.NotEmpty(readers); + Assert.True(debuggingSession.GetTestAccessor().HasProjectEmitBaseline(projectId)); + + CommitSolutionUpdate(debuggingSession); + } + else + { + // baseline is not present: + Assert.Empty(debuggingSession.GetTestAccessor().GetBaselineModuleReaders()); + Assert.False(debuggingSession.GetTestAccessor().HasProjectEmitBaseline(projectId)); + } + + // change the source (rude edit): + solution = solution.WithDocumentText(documentId, CreateText(source3)); + + // Rude Edits reported: + var diagnostics = await service.GetDocumentDiagnosticsAsync(solution.GetRequiredDocument(documentId), s_noActiveSpans, CancellationToken.None); + AssertEx.Equal( + ["ENC0023: " + string.Format(FeaturesResources.Adding_an_abstract_0_or_overriding_an_inherited_0_requires_restarting_the_application, FeaturesResources.method)], + diagnostics.Select(d => $"{d.Id}: {d.GetMessage()}")); + + // validate solution update status and emit: + result = await debuggingSession.EmitSolutionUpdateAsync(solution, runningProjects: [projectId], s_noActiveSpans, CancellationToken.None); + Assert.Equal(ModuleUpdateStatus.RestartRequired, result.ModuleUpdates.Status); + AssertEx.Equal([projectId], result.ProjectsToRebuild); + AssertEx.Equal([projectId], result.ProjectsToRestart); + + // restart: + _debuggerService.LoadedModules.Remove(moduleId); + moduleId = EmitAndLoadLibraryToDebuggee(source3, sourceFilePath: sourceFile.Path); + debuggingSession.UpdateBaselines(solution, result.ProjectsToRebuild); + + if (validChangeBeforeRudeEdit) + { + // baseline should be removed: + Assert.False(debuggingSession.GetTestAccessor().HasProjectEmitBaseline(projectId)); + + // all readers created for the module must be disposed, so we can rebuild the module: + VerifyReadersDisposed(readers); + } + + // no rude edits reported: + Assert.Empty(await service.GetDocumentDiagnosticsAsync(solution.GetRequiredDocument(documentId), s_noActiveSpans, CancellationToken.None)); + + // change the source (valid edit): + solution = solution.WithDocumentText(documentId, CreateText(source4)); + + // no rude edits: + Assert.Empty(await service.GetDocumentDiagnosticsAsync(solution.GetRequiredDocument(documentId), s_noActiveSpans, CancellationToken.None)); + + // apply valid change: + result = await debuggingSession.EmitSolutionUpdateAsync(solution, runningProjects: [projectId], s_noActiveSpans, CancellationToken.None); + Assert.Equal(ModuleUpdateStatus.Ready, result.ModuleUpdates.Status); + CommitSolutionUpdate(debuggingSession); + + EndDebuggingSession(debuggingSession); + + AssertEx.SequenceEqual(validChangeBeforeRudeEdit + ? [ + "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=3|EmptyHotReloadSessionCount=1", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges={6A6F7270-0000-4000-8000-000000000000}|ProjectIdsWithUpdatedBaselines=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=3|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines={6A6F7270-0000-4000-8000-000000000000}", + "Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=3|RudeEditKind=23|RudeEditSyntaxKind=8875|RudeEditBlocking=True|RudeEditProjectId={6A6F7270-0000-4000-8000-000000000000}", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=4|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges={6A6F7270-0000-4000-8000-000000000000}|ProjectIdsWithUpdatedBaselines=", + ] + : + [ + "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=2|EmptyHotReloadSessionCount=1", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines={6A6F7270-0000-4000-8000-000000000000}", + "Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=23|RudeEditSyntaxKind=8875|RudeEditBlocking=True|RudeEditProjectId={6A6F7270-0000-4000-8000-000000000000}", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=3|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges={6A6F7270-0000-4000-8000-000000000000}|ProjectIdsWithUpdatedBaselines=", + ], + _telemetryLog); + } + [Fact] public async Task SyntaxError() { @@ -1364,7 +1475,7 @@ public async Task SyntaxError() AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=1", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=True|HadRudeEdits=False|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=" + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=True|HadRudeEdits=False|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=" ], _telemetryLog); } @@ -1406,10 +1517,10 @@ public async Task SemanticError() AssertEx.SetEqual([moduleId], debuggingSession.GetTestAccessor().GetModulesPreparedForUpdate()); - AssertEx.Equal( + AssertEx.SequenceEqual( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=1", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_EmitDeltaErrorId: SessionId=1|EditSessionId=2|ErrorId=CS0266" ], _telemetryLog); } @@ -2057,7 +2168,7 @@ public async Task ValidSignificantChange_EmitError() AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=1", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_EmitDeltaErrorId: SessionId=1|EditSessionId=2|ErrorId=CS8055" ], _telemetryLog); } @@ -2457,18 +2568,18 @@ void ValidateDelta(ManagedHotReloadUpdate delta) if (breakMode) { - AssertEx.Equal( + AssertEx.SequenceEqual( [ $"Debugging_EncSession: SolutionSessionId={{00000000-AAAA-AAAA-AAAA-000000000000}}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount={(commitUpdate ? 3 : 2)}", - $"Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges={(commitUpdate ? "{6A6F7270-0000-4000-8000-000000000000}" : "")}", + $"Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges={(commitUpdate ? "{6A6F7270-0000-4000-8000-000000000000}" : "")}|ProjectIdsWithUpdatedBaselines=", ], _telemetryLog); } else { - AssertEx.Equal( + AssertEx.SequenceEqual( [ $"Debugging_EncSession: SolutionSessionId={{00000000-AAAA-AAAA-AAAA-000000000000}}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=1|EmptyHotReloadSessionCount={(commitUpdate ? 1 : 0)}", - $"Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges={(commitUpdate ? "{6A6F7270-0000-4000-8000-000000000000}" : "")}" + $"Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=31|ProjectIdsWithAppliedChanges={(commitUpdate ? "{6A6F7270-0000-4000-8000-000000000000}" : "")}|ProjectIdsWithUpdatedBaselines=" ], _telemetryLog); } } @@ -2741,7 +2852,7 @@ class C { int Y => 2; } solution = solution.WithDocumentText(document1.Id, CreateText(sourceV2)); // validate solution update status and emit: - var results = (await debuggingSession.EmitSolutionUpdateAsync(solution, s_noActiveSpans, CancellationToken.None).ConfigureAwait(false)).Dehydrate(); + var results = (await debuggingSession.EmitSolutionUpdateAsync(solution, runningProjects: [], s_noActiveSpans, CancellationToken.None).ConfigureAwait(false)).Dehydrate(); var diagnostics = results.GetAllDiagnostics(); var generatedFilePath = Path.Combine( @@ -3258,7 +3369,7 @@ public async Task ValidSignificantChange_BaselineCreationFailed_AssemblyReadErro AssertEx.Equal( [ "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=1|EmptySessionCount=0|HotReloadSessionCount=0|EmptyHotReloadSessionCount=1", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=1|InBreakState=True|Capabilities=31|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=", "Debugging_EncSession_EditSession_EmitDeltaErrorId: SessionId=1|EditSessionId=2|ErrorId=ENC1001" ], _telemetryLog); } @@ -3939,6 +4050,7 @@ static void F() ], InspectNonRemappableRegions(debuggingSession.EditSession.NonRemappableRegions)); ExitBreakState(debuggingSession); + EndDebuggingSession(debuggingSession); } /// @@ -4058,6 +4170,7 @@ static void F() ], InspectNonRemappableRegions(debuggingSession.EditSession.NonRemappableRegions)); ExitBreakState(debuggingSession); + EndDebuggingSession(debuggingSession); } /// @@ -4135,6 +4248,7 @@ static void F() ], spans); ExitBreakState(debuggingSession); + EndDebuggingSession(debuggingSession); } [Fact] @@ -4175,14 +4289,14 @@ public async Task MultiSession() var solution1 = solution.WithDocumentText(documentIdA, CreateText("class C { void M() { System.Console.WriteLine(" + i + "); } }")); - var result1 = await encService.EmitSolutionUpdateAsync(sessionId, solution1, s_noActiveSpans, CancellationToken.None); + var result1 = await encService.EmitSolutionUpdateAsync(sessionId, solution1, runningProjects: [], s_noActiveSpans, CancellationToken.None); Assert.Empty(result1.Diagnostics); Assert.Equal(1, result1.ModuleUpdates.Updates.Length); encService.DiscardSolutionUpdate(sessionId); var solution2 = solution1.WithDocumentText(documentIdA, CreateText(source3)); - var result2 = await encService.EmitSolutionUpdateAsync(sessionId, solution2, s_noActiveSpans, CancellationToken.None); + var result2 = await encService.EmitSolutionUpdateAsync(sessionId, solution2, runningProjects: [], s_noActiveSpans, CancellationToken.None); Assert.Equal("CS0103", result2.Diagnostics.Single().Diagnostics.Single().Id); Assert.Empty(result2.ModuleUpdates.Updates); @@ -4205,7 +4319,7 @@ public async Task Disposal() EndDebuggingSession(debuggingSession); // The folling methods shall not be called after the debugging session ended. - await Assert.ThrowsAsync(async () => await debuggingSession.EmitSolutionUpdateAsync(solution, s_noActiveSpans, CancellationToken.None)); + await Assert.ThrowsAsync(async () => await debuggingSession.EmitSolutionUpdateAsync(solution, runningProjects: [], s_noActiveSpans, CancellationToken.None)); Assert.Throws(() => debuggingSession.BreakStateOrCapabilitiesChanged(inBreakState: true)); Assert.Throws(() => debuggingSession.DiscardSolutionUpdate()); Assert.Throws(() => debuggingSession.CommitSolutionUpdate()); diff --git a/src/Features/Test/EditAndContinue/EmitSolutionUpdateResultsTests.cs b/src/Features/Test/EditAndContinue/EmitSolutionUpdateResultsTests.cs index 7a79ae129f31d..9f52adbc0541c 100644 --- a/src/Features/Test/EditAndContinue/EmitSolutionUpdateResultsTests.cs +++ b/src/Features/Test/EditAndContinue/EmitSolutionUpdateResultsTests.cs @@ -54,6 +54,8 @@ private static EmitSolutionUpdateResults CreateMockResults(Solution solution, IE RudeEdits = [.. rudeEdits.Select(id => new ProjectDiagnostics(id, [Diagnostic.Create(EditAndContinueDiagnosticDescriptors.GetDescriptor(RudeEditKind.InternalError), location: null)]))], Diagnostics = [], SyntaxError = null, + ProjectsToRebuild = [], + ProjectsToRestart = [], }; [Fact] @@ -135,7 +137,9 @@ public async Task GetHotReloadDiagnostics() Diagnostics = diagnostics, RudeEdits = rudeEdits, SyntaxError = syntaxError, - ModuleUpdates = new ModuleUpdates(ModuleUpdateStatus.Blocked, Updates: []) + ModuleUpdates = new ModuleUpdates(ModuleUpdateStatus.Blocked, Updates: []), + ProjectsToRebuild = [], + ProjectsToRestart = [], }; var actual = data.GetAllDiagnostics(); @@ -161,13 +165,16 @@ public void RunningProjects_Updates() .AddTestProject("A", out var a).AddProjectReferences([new(c)]).Solution .AddTestProject("B", out var b).AddProjectReferences([new(c), new(d)]).Solution; - var runningProjects = new[] { a, b }; + var runningProjects = new[] { a, b }.ToImmutableHashSet(); var results = CreateMockResults(solution, updates: [c, d], rudeEdits: []); - var projectsToRestart = new HashSet(); - var projectsToRebuild = new HashSet(); - - results.GetProjectsToRebuildAndRestart(solution, p => runningProjects.Contains(p.Id), projectsToRestart, projectsToRebuild); + EmitSolutionUpdateResults.GetProjectsToRebuildAndRestart( + solution, + results.ModuleUpdates, + results.RudeEdits, + runningProjects, + out var projectsToRestart, + out var projectsToRebuild); Assert.Empty(projectsToRestart); Assert.Empty(projectsToRebuild); @@ -184,19 +191,22 @@ public void RunningProjects_RudeEdits() .AddTestProject("A", out var a).AddProjectReferences([new(c)]).Solution .AddTestProject("B", out var b).AddProjectReferences([new(c), new(d)]).Solution; - var runningProjects = new[] { a, b }; + var runningProjects = new[] { a, b }.ToImmutableHashSet(); var results = CreateMockResults(solution, updates: [], rudeEdits: [d]); - var projectsToRestart = new HashSet(); - var projectsToRebuild = new HashSet(); - - results.GetProjectsToRebuildAndRestart(solution, p => runningProjects.Contains(p.Id), projectsToRestart, projectsToRebuild); + EmitSolutionUpdateResults.GetProjectsToRebuildAndRestart( + solution, + results.ModuleUpdates, + results.RudeEdits, + runningProjects, + out var projectsToRestart, + out var projectsToRebuild); // D has rude edit ==> B has to restart - AssertEx.SetEqual([b], projectsToRestart.Select(p => p.Id)); + AssertEx.SetEqual([b], projectsToRestart); // D has rude edit: - AssertEx.SetEqual([d], projectsToRebuild.Select(p => p.Id)); + AssertEx.SetEqual([d], projectsToRebuild); } [Fact] @@ -210,13 +220,16 @@ public void RunningProjects_RudeEdits_NotImpactingRunningProjects() .AddTestProject("A", out var a).AddProjectReferences([new(c)]).Solution .AddTestProject("B", out var b).AddProjectReferences([new(c), new(d)]).Solution; - var runningProjects = new[] { a }; + var runningProjects = new[] { a }.ToImmutableHashSet(); var results = CreateMockResults(solution, updates: [], rudeEdits: [d]); - var projectsToRestart = new HashSet(); - var projectsToRebuild = new HashSet(); - - results.GetProjectsToRebuildAndRestart(solution, p => runningProjects.Contains(p.Id), projectsToRestart, projectsToRebuild); + EmitSolutionUpdateResults.GetProjectsToRebuildAndRestart( + solution, + results.ModuleUpdates, + results.RudeEdits, + runningProjects, + out var projectsToRestart, + out var projectsToRebuild); Assert.Empty(projectsToRestart); Assert.Empty(projectsToRebuild); @@ -233,20 +246,23 @@ public void RunningProjects_RudeEditAndUpdate_Dependent() .AddTestProject("A", out var a).AddProjectReferences([new(c)]).Solution .AddTestProject("B", out var b).AddProjectReferences([new(c), new(d)]).Solution; - var runningProjects = new[] { a, b }; + var runningProjects = new[] { a, b }.ToImmutableHashSet(); var results = CreateMockResults(solution, updates: [c], rudeEdits: [d]); - var projectsToRestart = new HashSet(); - var projectsToRebuild = new HashSet(); - - results.GetProjectsToRebuildAndRestart(solution, p => runningProjects.Contains(p.Id), projectsToRestart, projectsToRebuild); + EmitSolutionUpdateResults.GetProjectsToRebuildAndRestart( + solution, + results.ModuleUpdates, + results.RudeEdits, + runningProjects, + out var projectsToRestart, + out var projectsToRebuild); // D has rude edit => B has to restart // C has update, B -> C and A -> C ==> A has to restart - AssertEx.SetEqual([a, b], projectsToRestart.Select(p => p.Id)); + AssertEx.SetEqual([a, b], projectsToRestart); // D has rude edit, C has update that impacts restart set: - AssertEx.SetEqual([c, d], projectsToRebuild.Select(p => p.Id)); + AssertEx.SetEqual([c, d], projectsToRebuild); } [Fact] @@ -260,19 +276,22 @@ public void RunningProjects_RudeEditAndUpdate_Independent() .AddTestProject("A", out var a).AddProjectReferences([new(c)]).Solution .AddTestProject("B", out var b).AddProjectReferences([new(d)]).Solution; - var runningProjects = new[] { a, b }; + var runningProjects = new[] { a, b }.ToImmutableHashSet(); var results = CreateMockResults(solution, updates: [c], rudeEdits: [d]); - var projectsToRestart = new HashSet(); - var projectsToRebuild = new HashSet(); - - results.GetProjectsToRebuildAndRestart(solution, p => runningProjects.Contains(p.Id), projectsToRestart, projectsToRebuild); + EmitSolutionUpdateResults.GetProjectsToRebuildAndRestart( + solution, + results.ModuleUpdates, + results.RudeEdits, + runningProjects, + out var projectsToRestart, + out var projectsToRebuild); // D has rude edit => B has to restart - AssertEx.SetEqual([b], projectsToRestart.Select(p => p.Id)); + AssertEx.SetEqual([b], projectsToRestart); // D has rude edit, C has update that does not impacts restart set: - AssertEx.SetEqual([d], projectsToRebuild.Select(p => p.Id)); + AssertEx.SetEqual([d], projectsToRebuild); } [Theory] @@ -291,16 +310,19 @@ public void RunningProjects_RudeEditAndUpdate_Chain(bool reverse) .AddTestProject("R3", out var r3).AddProjectReferences([new(p3), new(p4)]).Solution .AddTestProject("R4", out var r4).AddProjectReferences([new(p4)]).Solution; - var runningProjects = new[] { r1, r2, r3, r4 }; + var runningProjects = new[] { r1, r2, r3, r4 }.ToImmutableHashSet(); var results = CreateMockResults(solution, updates: reverse ? [p4, p3, p2] : [p2, p3, p4], rudeEdits: [p1]); - var projectsToRestart = new HashSet(); - var projectsToRebuild = new HashSet(); - - results.GetProjectsToRebuildAndRestart(solution, p => runningProjects.Contains(p.Id), projectsToRestart, projectsToRebuild); + EmitSolutionUpdateResults.GetProjectsToRebuildAndRestart( + solution, + results.ModuleUpdates, + results.RudeEdits, + runningProjects, + out var projectsToRestart, + out var projectsToRebuild); - AssertEx.SetEqual([r1, r2, r3, r4], projectsToRestart.Select(p => p.Id)); - AssertEx.SetEqual([p1, p2, p3, p4], projectsToRebuild.Select(p => p.Id)); + AssertEx.SetEqual([r1, r2, r3, r4], projectsToRestart); + AssertEx.SetEqual([p1, p2, p3, p4], projectsToRebuild); } } } diff --git a/src/Features/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs b/src/Features/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs index 472aef5156b4f..ab25d52016512 100644 --- a/src/Features/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs +++ b/src/Features/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs @@ -173,11 +173,12 @@ await localWorkspace.ChangeSolutionAsync(localWorkspace.CurrentSolution var diagnosticDescriptor1 = EditAndContinueDiagnosticDescriptors.GetDescriptor(EditAndContinueErrorCode.ErrorReadingFile); - mockEncService.EmitSolutionUpdateImpl = (solution, activeStatementSpanProvider) => + mockEncService.EmitSolutionUpdateImpl = (solution, runningProjects, activeStatementSpanProvider) => { var project = solution.GetRequiredProject(projectId); Assert.Equal("proj", project.Name); AssertEx.Equal(activeSpans1, activeStatementSpanProvider(documentId, "test.cs", CancellationToken.None).AsTask().Result); + AssertEx.Equal([project.Id], runningProjects); var deltas = ImmutableArray.Create(new ManagedHotReloadUpdate( module: moduleId1, @@ -209,11 +210,13 @@ await localWorkspace.ChangeSolutionAsync(localWorkspace.CurrentSolution ModuleUpdates = updates, Diagnostics = diagnostics, RudeEdits = [], - SyntaxError = syntaxError + SyntaxError = syntaxError, + ProjectsToRebuild = [project.Id], + ProjectsToRestart = [project.Id], }; }; - var results = await sessionProxy.EmitSolutionUpdateAsync(localWorkspace.CurrentSolution, activeStatementSpanProvider, CancellationToken.None); + var results = await sessionProxy.EmitSolutionUpdateAsync(localWorkspace.CurrentSolution, runningProjects: [project.Id], activeStatementSpanProvider, CancellationToken.None); AssertEx.Equal($"[{projectId}] Error ENC1001: test.cs(0, 1, 0, 2): {string.Format(FeaturesResources.ErrorReadingFile, "doc", "syntax error")}", Inspect(results.SyntaxError!)); Assert.Equal(ModuleUpdateStatus.Ready, results.ModuleUpdates.Status); diff --git a/src/Features/Test/EditAndContinue/WatchHotReloadServiceTests.cs b/src/Features/Test/EditAndContinue/WatchHotReloadServiceTests.cs index 364197f4a8a09..60062cf1b21f2 100644 --- a/src/Features/Test/EditAndContinue/WatchHotReloadServiceTests.cs +++ b/src/Features/Test/EditAndContinue/WatchHotReloadServiceTests.cs @@ -74,7 +74,7 @@ public async Task Test() // Valid update: solution = solution.WithDocumentText(documentIdA, CreateText(source2)); - var result = await hotReload.GetUpdatesAsync(solution, isRunningProject: _ => false, CancellationToken.None); + var result = await hotReload.GetUpdatesAsync(solution, runningProjects: [], CancellationToken.None); Assert.Empty(result.Diagnostics); Assert.Equal(1, result.ProjectUpdates.Length); AssertEx.Equal([0x02000002], result.ProjectUpdates[0].UpdatedTypes); @@ -82,35 +82,35 @@ public async Task Test() // Rude edit: solution = solution.WithDocumentText(documentIdA, CreateText(source3)); - result = await hotReload.GetUpdatesAsync(solution, isRunningProject: _ => true, CancellationToken.None); + result = await hotReload.GetUpdatesAsync(solution, runningProjects: solution.ProjectIds.ToImmutableHashSet(), CancellationToken.None); AssertEx.Equal( ["ENC0110: " + string.Format(FeaturesResources.Changing_the_signature_of_0_requires_restarting_the_application_because_it_is_not_supported_by_the_runtime, FeaturesResources.method)], result.Diagnostics.Select(d => $"{d.Id}: {d.GetMessage()}")); Assert.Empty(result.ProjectUpdates); - AssertEx.SetEqual(["P"], result.ProjectsToRestart.Select(p => p.Name)); - AssertEx.SetEqual(["P"], result.ProjectsToRebuild.Select(p => p.Name)); + AssertEx.SetEqual(["P"], result.ProjectIdsToRestart.Select(p => solution.GetRequiredProject(p).Name)); + AssertEx.SetEqual(["P"], result.ProjectIdsToRebuild.Select(p => solution.GetRequiredProject(p).Name)); // Syntax error: solution = solution.WithDocumentText(documentIdA, CreateText(source4)); - result = await hotReload.GetUpdatesAsync(solution, isRunningProject: _ => true, CancellationToken.None); + result = await hotReload.GetUpdatesAsync(solution, runningProjects: solution.ProjectIds.ToImmutableHashSet(), CancellationToken.None); AssertEx.Equal( ["CS1002: " + CSharpResources.ERR_SemicolonExpected], result.Diagnostics.Select(d => $"{d.Id}: {d.GetMessage()}")); Assert.Empty(result.ProjectUpdates); - Assert.Empty(result.ProjectsToRestart); - Assert.Empty(result.ProjectsToRebuild); + Assert.Empty(result.ProjectIdsToRestart); + Assert.Empty(result.ProjectIdsToRebuild); // Semantic error: solution = solution.WithDocumentText(documentIdA, CreateText(source5)); - result = await hotReload.GetUpdatesAsync(solution, isRunningProject: _ => true, CancellationToken.None); + result = await hotReload.GetUpdatesAsync(solution, runningProjects: solution.ProjectIds.ToImmutableHashSet(), CancellationToken.None); AssertEx.Equal( ["CS0103: " + string.Format(CSharpResources.ERR_NameNotInContext, "Unknown")], result.Diagnostics.Select(d => $"{d.Id}: {d.GetMessage()}")); Assert.Empty(result.ProjectUpdates); - Assert.Empty(result.ProjectsToRestart); - Assert.Empty(result.ProjectsToRebuild); + Assert.Empty(result.ProjectIdsToRestart); + Assert.Empty(result.ProjectIdsToRebuild); hotReload.EndSession(); } @@ -162,7 +162,7 @@ public async Task SourceGeneratorFailure() solution = solution.WithAdditionalDocumentText(aId, CreateText("updated text")); - var result = await hotReload.GetUpdatesAsync(solution, isRunningProject: _ => true, CancellationToken.None); + var result = await hotReload.GetUpdatesAsync(solution, runningProjects: solution.ProjectIds.ToImmutableHashSet(), CancellationToken.None); var diagnostic = result.Diagnostics.Single(); Assert.Equal("CS8785", diagnostic.Id); Assert.Contains("Source generator failed", diagnostic.GetMessage()); diff --git a/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs b/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs index 83dedbd3788c9..eb6e9ac492dc2 100644 --- a/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs +++ b/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs @@ -31,7 +31,7 @@ namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests; -public abstract class EditAndContinueWorkspaceTestBase : TestBase +public abstract class EditAndContinueWorkspaceTestBase : TestBase, IDisposable { private protected static readonly Guid s_solutionTelemetryId = Guid.Parse("00000000-AAAA-AAAA-AAAA-000000000000"); private protected static readonly Guid s_defaultProjectTelemetryId = Guid.Parse("00000000-AAAA-AAAA-AAAA-111111111111"); @@ -51,6 +51,21 @@ public abstract class EditAndContinueWorkspaceTestBase : TestBase LoadedModules = [] }; + /// + /// Streams that are verified to be disposed at the end of the debug session (by default). + /// + private ImmutableList _disposalVerifiedStreams = []; + + public override void Dispose() + { + base.Dispose(); + + foreach (var stream in _disposalVerifiedStreams) + { + Assert.False(stream.CanRead); + } + } + internal TestWorkspace CreateWorkspace(out Solution solution, out EditAndContinueService service, Type[]? additionalParts = null) { var composition = FeaturesTestCompositions.Features @@ -185,6 +200,9 @@ internal static void CapabilitiesChanged(DebuggingSession session) internal static void CommitSolutionUpdate(DebuggingSession session) => session.CommitSolutionUpdate(); + internal static void DiscardSolutionUpdate(DebuggingSession session) + => session.DiscardSolutionUpdate(); + internal static void EndDebuggingSession(DebuggingSession session) => session.EndSession(out _); @@ -193,7 +211,7 @@ internal static void EndDebuggingSession(DebuggingSession session) Solution solution, ActiveStatementSpanProvider? activeStatementSpanProvider = null) { - var result = await session.EmitSolutionUpdateAsync(solution, activeStatementSpanProvider ?? s_noActiveSpans, CancellationToken.None); + var result = await session.EmitSolutionUpdateAsync(solution, runningProjects: [], activeStatementSpanProvider ?? s_noActiveSpans, CancellationToken.None); return (result.ModuleUpdates, result.Diagnostics.ToDiagnosticData(solution)); } @@ -290,11 +308,13 @@ internal Guid EmitLibrary(Compilation compilation, DebugInformationFormat pdbFor var moduleId = moduleMetadata.GetModuleVersionId(); // associate the binaries with the project (assumes a single project) + _mockCompilationOutputsProvider = _ => new MockCompilationOutputs(moduleId) { OpenAssemblyStreamImpl = () => { var stream = new MemoryStream(); + ImmutableInterlocked.Update(ref _disposalVerifiedStreams, s => s.Add(stream)); peImage.WriteToStream(stream); stream.Position = 0; return stream; @@ -302,6 +322,7 @@ internal Guid EmitLibrary(Compilation compilation, DebugInformationFormat pdbFor OpenPdbStreamImpl = () => { var stream = new MemoryStream(); + ImmutableInterlocked.Update(ref _disposalVerifiedStreams, s => s.Add(stream)); pdbImage.WriteToStream(stream); stream.Position = 0; return stream; diff --git a/src/Features/TestUtilities/EditAndContinue/Extensions.cs b/src/Features/TestUtilities/EditAndContinue/Extensions.cs index ac5df24da0eb4..d8903edb98394 100644 --- a/src/Features/TestUtilities/EditAndContinue/Extensions.cs +++ b/src/Features/TestUtilities/EditAndContinue/Extensions.cs @@ -85,7 +85,7 @@ public static Guid CreateProjectTelemetryId(string projectName) public static ProjectInfo CreateProjectInfo(string projectName, string language = LanguageNames.CSharp) => ProjectInfo.Create( - ProjectId.CreateNewId(), + ProjectId.CreateNewId(debugName: projectName), VersionStamp.Create(), name: projectName, assemblyName: projectName, @@ -97,6 +97,7 @@ public static ProjectInfo CreateProjectInfo(string projectName, string language NoCompilationConstants.LanguageName => null, _ => throw ExceptionUtilities.UnexpectedValue(language) }, + compilationOptions: TestOptions.DebugDll, filePath: projectName + language switch { LanguageNames.CSharp => ".csproj", diff --git a/src/Features/TestUtilities/EditAndContinue/MockEditAndContinueService.cs b/src/Features/TestUtilities/EditAndContinue/MockEditAndContinueService.cs index 2128da2192701..27423d63fc4ff 100644 --- a/src/Features/TestUtilities/EditAndContinue/MockEditAndContinueService.cs +++ b/src/Features/TestUtilities/EditAndContinue/MockEditAndContinueService.cs @@ -23,9 +23,10 @@ internal class MockEditAndContinueService() : IEditAndContinueService public Func, bool, bool, DebuggingSessionId>? StartDebuggingSessionImpl; public Action? EndDebuggingSessionImpl; - public Func? EmitSolutionUpdateImpl; + public Func, ActiveStatementSpanProvider, EmitSolutionUpdateResults>? EmitSolutionUpdateImpl; public Action? OnSourceFileUpdatedImpl; public Action? CommitSolutionUpdateImpl; + public Action>? UpdateBaselinesImpl; public Action? BreakStateOrCapabilitiesChangedImpl; public Action? DiscardSolutionUpdateImpl; public Func>? GetDocumentDiagnosticsImpl; @@ -39,8 +40,11 @@ public void CommitSolutionUpdate(DebuggingSessionId sessionId) public void DiscardSolutionUpdate(DebuggingSessionId sessionId) => DiscardSolutionUpdateImpl?.Invoke(); - public ValueTask EmitSolutionUpdateAsync(DebuggingSessionId sessionId, Solution solution, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) - => new((EmitSolutionUpdateImpl ?? throw new NotImplementedException()).Invoke(solution, activeStatementSpanProvider)); + public void UpdateBaselines(DebuggingSessionId sessionId, Solution solution, ImmutableArray rebuiltProjects) + => UpdateBaselinesImpl?.Invoke(solution, rebuiltProjects); + + public ValueTask EmitSolutionUpdateAsync(DebuggingSessionId sessionId, Solution solution, IImmutableSet runningProjects, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) + => new((EmitSolutionUpdateImpl ?? throw new NotImplementedException()).Invoke(solution, runningProjects, activeStatementSpanProvider)); public void EndDebuggingSession(DebuggingSessionId sessionId) => EndDebuggingSessionImpl?.Invoke(); diff --git a/src/Features/VisualBasic/Portable/EmbeddedLanguages/VisualBasicEmbeddedLanguagesProvider.vb b/src/Features/VisualBasic/Portable/EmbeddedLanguages/VisualBasicEmbeddedLanguagesProvider.vb index 25524d719e0f4..9c5e868e15811 100644 --- a/src/Features/VisualBasic/Portable/EmbeddedLanguages/VisualBasicEmbeddedLanguagesProvider.vb +++ b/src/Features/VisualBasic/Portable/EmbeddedLanguages/VisualBasicEmbeddedLanguagesProvider.vb @@ -11,10 +11,11 @@ Imports Microsoft.CodeAnalysis.VisualBasic.LanguageService Namespace Microsoft.CodeAnalysis.VisualBasic.EmbeddedLanguages.LanguageServices - Friend Class VisualBasicEmbeddedLanguagesProvider + Friend NotInheritable Class VisualBasicEmbeddedLanguagesProvider Inherits AbstractEmbeddedLanguagesProvider Public Shared ReadOnly Info As New EmbeddedLanguageInfo( + VisualBasicBlockFacts.Instance, VisualBasicSyntaxFacts.Instance, VisualBasicSemanticFactsService.Instance, VisualBasicVirtualCharService.Instance) diff --git a/src/Features/VisualBasic/Portable/GenerateType/VisualBasicGenerateTypeService.vb b/src/Features/VisualBasic/Portable/GenerateType/VisualBasicGenerateTypeService.vb index ec54305a08db7..f0bf267e39a6f 100644 --- a/src/Features/VisualBasic/Portable/GenerateType/VisualBasicGenerateTypeService.vb +++ b/src/Features/VisualBasic/Portable/GenerateType/VisualBasicGenerateTypeService.vb @@ -539,23 +539,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.GenerateType Return False End If - Dim node As SyntaxNode = expression - While node IsNot Nothing - If TypeOf node Is InheritsStatementSyntax Then - If node.Parent IsNot Nothing AndAlso TypeOf node.Parent Is InterfaceBlockSyntax Then - typeKindValue = TypeKindOptions.Interface - Return True - End If - - typeKindValue = TypeKindOptions.Class - Return True - ElseIf TypeOf node Is ImplementsStatementSyntax Then + If TypeOf expression.Parent Is InheritsStatementSyntax Then + If expression.Parent.Parent IsNot Nothing AndAlso TypeOf expression.Parent.Parent Is InterfaceBlockSyntax Then typeKindValue = TypeKindOptions.Interface Return True End If - node = node.Parent - End While + typeKindValue = TypeKindOptions.Class + Return True + ElseIf TypeOf expression.Parent Is ImplementsStatementSyntax Then + typeKindValue = TypeKindOptions.Interface + Return True + End If Return False End Function diff --git a/src/Features/VisualBasic/Portable/Structure/VisualBasicBlockStructureProvider.vb b/src/Features/VisualBasic/Portable/Structure/VisualBasicBlockStructureProvider.vb index 27f1be67f2a92..68801154fcc2b 100644 --- a/src/Features/VisualBasic/Portable/Structure/VisualBasicBlockStructureProvider.vb +++ b/src/Features/VisualBasic/Portable/Structure/VisualBasicBlockStructureProvider.vb @@ -7,7 +7,7 @@ Imports Microsoft.CodeAnalysis.Structure Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Structure - Friend Class VisualBasicBlockStructureProvider + Friend NotInheritable Class VisualBasicBlockStructureProvider Inherits AbstractBlockStructureProvider Public Shared Function CreateDefaultNodeStructureProviderMap() As ImmutableDictionary(Of Type, ImmutableArray(Of AbstractSyntaxStructureProvider)) diff --git a/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb b/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb index 0ba154e235d0f..7bbfd6ed38090 100644 --- a/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb @@ -16,7 +16,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.UseAutoProperty Friend NotInheritable Class VisualBasicUseAutoPropertyCodeFixProvider - Inherits AbstractUseAutoPropertyCodeFixProvider(Of TypeBlockSyntax, PropertyBlockSyntax, ModifiedIdentifierSyntax, ConstructorBlockSyntax, ExpressionSyntax) + Inherits AbstractUseAutoPropertyCodeFixProvider(Of VisualBasicUseAutoPropertyCodeFixProvider, TypeBlockSyntax, PropertyBlockSyntax, ModifiedIdentifierSyntax, ConstructorBlockSyntax, ExpressionSyntax) diff --git a/src/Interactive/HostProcess/x64/InteractiveHost64.csproj b/src/Interactive/HostProcess/x64/InteractiveHost64.csproj index f2e640e639115..14323ad105381 100644 --- a/src/Interactive/HostProcess/x64/InteractiveHost64.csproj +++ b/src/Interactive/HostProcess/x64/InteractiveHost64.csproj @@ -26,8 +26,11 @@ - - + + diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs index 41fe21c197552..9e3a07e88d383 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs @@ -61,7 +61,7 @@ public async Task CreatingDirectoryWatchRequestsDirectoryWatch() var tempDirectory = tempRoot.CreateDirectory(); // Try creating a context and ensure we created the registration - var context = lspFileChangeWatcher.CreateContext([new ProjectSystem.WatchedDirectory(tempDirectory.Path, extensionFilter: null)]); + var context = lspFileChangeWatcher.CreateContext([new ProjectSystem.WatchedDirectory(tempDirectory.Path, extensionFilters: [])]); await WaitForFileWatcherAsync(testLspServer); var watcher = GetSingleFileWatcher(dynamicCapabilitiesRpcTarget); diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs index 8ad3f78c6dac2..30de915c94972 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs @@ -30,6 +30,7 @@ internal class ServiceBrokerFactory { private BrokeredServiceContainer? _container; private readonly ExportProvider _exportProvider; + private readonly WrappedServiceBroker _wrappedServiceBroker; private Task _bridgeCompletionTask; private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); private readonly ImmutableArray _onServiceBrokerInitialized; @@ -37,19 +38,15 @@ internal class ServiceBrokerFactory [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public ServiceBrokerFactory([ImportMany] IEnumerable onServiceBrokerInitialized, - ExportProvider exportProvider) + ExportProvider exportProvider, + WrappedServiceBroker wrappedServiceBroker) { _exportProvider = exportProvider; _bridgeCompletionTask = Task.CompletedTask; _onServiceBrokerInitialized = onServiceBrokerInitialized.ToImmutableArray(); + _wrappedServiceBroker = wrappedServiceBroker; } - /// - /// Returns a full-access service broker, but will throw if we haven't yet connected to the Dev Kit broker. - /// - [Export(typeof(SVsFullAccessServiceBroker))] - public IServiceBroker FullAccessServiceBroker => this.GetRequiredServiceBrokerContainer().GetFullAccessServiceBroker(); - /// /// Returns a full-access service broker, but will return null if we haven't yet connected to the Dev Kit broker. /// @@ -69,6 +66,7 @@ public async Task CreateAsync() Contract.ThrowIfFalse(_container == null, "We should only create one container."); _container = await BrokeredServiceContainer.CreateAsync(_exportProvider, _cancellationTokenSource.Token); + _wrappedServiceBroker.SetServiceBroker(_container.GetFullAccessServiceBroker()); foreach (var onInitialized in _onServiceBrokerInitialized) { @@ -99,4 +97,3 @@ public Task ShutdownAndWaitForCompletionAsync() return _bridgeCompletionTask; } } -#pragma warning restore RS0030 // Do not used banned APIs diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/Descriptors.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/Descriptors.cs index 1fb946820d563..da7fbebc4ba6a 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/Descriptors.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/Descriptors.cs @@ -51,6 +51,7 @@ internal class Descriptors { BrokeredServiceDescriptors.MauiLaunchCustomizerService.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) }, { BrokeredServiceDescriptors.DebuggerSymbolLocatorService.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) }, { BrokeredServiceDescriptors.DebuggerSourceLinkService.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) }, + { BrokeredServiceDescriptors.ProjectSystemQueryExecutionService.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) }, }.ToImmutableDictionary(); public static ServiceJsonRpcDescriptor CreateDescriptor(ServiceMoniker serviceMoniker) => new( diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs new file mode 100644 index 0000000000000..5acad362275c8 --- /dev/null +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Composition; +using System.IO.Pipelines; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.ServiceHub.Framework; +using Microsoft.VisualStudio.Composition; +using Microsoft.VisualStudio.Shell.ServiceBroker; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.LanguageServer.BrokeredServices; +#pragma warning restore RS0030 // Do not used banned APIs + +/// +/// Implements a wrapper around that allows clients to MEF import the service broker. +/// This wrapper will wait for the service broker to be available before invoking the requested method. +/// +[Export(typeof(SVsFullAccessServiceBroker)), Shared] +[Export(typeof(WrappedServiceBroker))] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal class WrappedServiceBroker() : IServiceBroker +{ + private readonly TaskCompletionSource _serviceBrokerTask = new(TaskCreationOptions.RunContinuationsAsynchronously); + + internal void SetServiceBroker(IServiceBroker serviceBroker) + { + Contract.ThrowIfTrue(_serviceBrokerTask.Task.IsCompleted); + serviceBroker.AvailabilityChanged += (s, e) => AvailabilityChanged?.Invoke(this, e); + _serviceBrokerTask.SetResult(serviceBroker); + } + + public event EventHandler? AvailabilityChanged; + + public async ValueTask GetPipeAsync(ServiceMoniker serviceMoniker, ServiceActivationOptions options = default, CancellationToken cancellationToken = default) + { + var serviceBroker = await _serviceBrokerTask.Task; + return await serviceBroker.GetPipeAsync(serviceMoniker, options, cancellationToken); + } + + public async ValueTask GetProxyAsync(ServiceRpcDescriptor serviceDescriptor, ServiceActivationOptions options = default, CancellationToken cancellationToken = default) where T : class + { + var serviceBroker = await _serviceBrokerTask.Task; +#pragma warning disable ISB001 // Dispose of proxies - caller is responsible for disposing the proxy. + return await serviceBroker.GetProxyAsync(serviceDescriptor, options, cancellationToken); +#pragma warning restore ISB001 // Dispose of proxies + } +} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs index 06451af2acaa3..c21b47a23e803 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs @@ -75,13 +75,23 @@ public FileChangeContext(ImmutableArray watchedDirectories, Ls // If we have any watched directories, then watch those directories directly if (watchedDirectories.Any()) { - var directoryWatches = watchedDirectories.Select(d => new FileSystemWatcher + var directoryWatches = watchedDirectories.Select(d => { - GlobPattern = new RelativePattern + var pattern = "**/*" + d.ExtensionFilters.Length switch { - BaseUri = ProtocolConversions.CreateRelativePatternBaseUri(d.Path), - Pattern = d.ExtensionFilter is not null ? "**/*" + d.ExtensionFilter : "**/*" - } + 0 => string.Empty, + 1 => d.ExtensionFilters[0], + _ => "{" + string.Join(',', d.ExtensionFilters) + "}" + }; + + return new FileSystemWatcher + { + GlobPattern = new RelativePattern + { + BaseUri = ProtocolConversions.CreateRelativePatternBaseUri(d.Path), + Pattern = pattern + } + }; }).ToArray(); _directoryWatchRegistration = new LspFileWatchRegistration(lspFileChangeWatcher, directoryWatches); diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/SimpleFileChangeWatcher.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/SimpleFileChangeWatcher.cs index 9cc6048699756..5fefb873a7367 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/SimpleFileChangeWatcher.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/SimpleFileChangeWatcher.cs @@ -46,8 +46,8 @@ public FileChangeContext(ImmutableArray watchedDirectories) var watcher = new FileSystemWatcher(watchedDirectory.Path); watcher.IncludeSubdirectories = true; - if (watchedDirectory.ExtensionFilter != null) - watcher.Filter = '*' + watchedDirectory.ExtensionFilter; + foreach (var filter in watchedDirectory.ExtensionFilters) + watcher.Filters.Add('*' + filter); watcher.Changed += RaiseEvent; watcher.Created += RaiseEvent; diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LoadedProject.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LoadedProject.cs index c41713fb1ac6a..0cdec8f2bd4f0 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LoadedProject.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LoadedProject.cs @@ -53,11 +53,7 @@ public LoadedProject(ProjectSystemProject projectSystemProject, SolutionServices // TODO: we only should listen for add/removals here, but we can't specify such a filter now _projectDirectory = Path.GetDirectoryName(_projectFilePath)!; - _fileChangeContext = fileWatcher.CreateContext([ - new(_projectDirectory, ".cs"), - new(_projectDirectory, ".cshtml"), - new(_projectDirectory, ".razor") - ]); + _fileChangeContext = fileWatcher.CreateContext([new(_projectDirectory, [".cs", ".cshtml", ".razor"])]); _fileChangeContext.FileChanged += FileChangedContext_FileChanged; // Start watching for file changes for the project file as well diff --git a/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs b/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs index 0f987aa808ff2..6da78c6e625f2 100644 --- a/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs +++ b/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs @@ -424,12 +424,12 @@ protected internal virtual void BeforeRequest(TRequest request) /// Provides an extensibility point to log or otherwise inspect errors thrown from non-mutating requests, /// which would otherwise be lost to the fire-and-forget task in the queue. /// - /// The task to be inspected. + /// The task to be inspected. /// If exceptions should be re-thrown. - /// The task from , to allow chained calls if needed. - public virtual Task WrapStartRequestTaskAsync(Task nonMutatingRequestTask, bool rethrowExceptions) + /// The task from , to allow chained calls if needed. + public virtual Task WrapStartRequestTaskAsync(Task requestTask, bool rethrowExceptions) { - return nonMutatingRequestTask; + return requestTask; } /// diff --git a/src/LanguageServer/Protocol/DefaultCapabilitiesProvider.cs b/src/LanguageServer/Protocol/DefaultCapabilitiesProvider.cs index 2dffadeb45af7..ae55d4bce2eff 100644 --- a/src/LanguageServer/Protocol/DefaultCapabilitiesProvider.cs +++ b/src/LanguageServer/Protocol/DefaultCapabilitiesProvider.cs @@ -53,6 +53,7 @@ public ServerCapabilities GetCapabilities(ClientCapabilities clientCapabilities) lz => CommonCompletionUtilities.GetTriggerCharacters(lz.Value)).Distinct().Select(c => c.ToString()).ToArray(); capabilities.DefinitionProvider = true; + capabilities.TypeDefinitionProvider = true; capabilities.DocumentHighlightProvider = true; capabilities.RenameProvider = new RenameOptions { diff --git a/src/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileHandler.cs b/src/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileHandler.cs index 1036b6cafa5dc..fb8bdf4533e68 100644 --- a/src/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileHandler.cs +++ b/src/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileHandler.cs @@ -59,6 +59,12 @@ public FormatNewFileHandler(IGlobalOptionService globalOptions) var document = solution.GetRequiredDocument(documentId); + return await GetFormattedNewFileContentAsync(document, cancellationToken).ConfigureAwait(false); + } + + internal static async Task GetFormattedNewFileContentAsync(Document document, CancellationToken cancellationToken) + { + var project = document.Project; // Run the new document formatting service, to make sure the right namespace type is used, among other things var formattingService = document.GetLanguageService(); if (formattingService is not null) @@ -79,7 +85,7 @@ public FormatNewFileHandler(IGlobalOptionService globalOptions) // Now format the document so indentation etc. is correct var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false); - root = Formatter.Format(root, solution.Services, syntaxFormattingOptions, cancellationToken); + root = Formatter.Format(root, project.Solution.Services, syntaxFormattingOptions, cancellationToken); return root.ToFullString(); } diff --git a/src/LanguageServer/Protocol/ExternalAccess/Razor/SimplifyMethodHandler.cs b/src/LanguageServer/Protocol/ExternalAccess/Razor/SimplifyMethodHandler.cs index 7d547f461e083..f9eae08e54024 100644 --- a/src/LanguageServer/Protocol/ExternalAccess/Razor/SimplifyMethodHandler.cs +++ b/src/LanguageServer/Protocol/ExternalAccess/Razor/SimplifyMethodHandler.cs @@ -43,9 +43,16 @@ public SimplifyMethodHandler() if (originalDocument is null) return null; + var textEdit = request.TextEdit; + + return await GetSimplifiedEditsAsync(originalDocument, textEdit, cancellationToken).ConfigureAwait(false); + } + + internal static async Task GetSimplifiedEditsAsync(Document originalDocument, TextEdit textEdit, CancellationToken cancellationToken) + { // Create a temporary syntax tree that includes the text edit. var originalSourceText = await originalDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); - var pendingChange = ProtocolConversions.TextEditToTextChange(request.TextEdit, originalSourceText); + var pendingChange = ProtocolConversions.TextEditToTextChange(textEdit, originalSourceText); var newSourceText = originalSourceText.WithChanges(pendingChange); var originalTree = await originalDocument.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var newTree = originalTree.WithChangedText(newSourceText); diff --git a/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs b/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs index be6077f5ed3b7..06a2c81bc987d 100644 --- a/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs +++ b/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs @@ -7,7 +7,6 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Host.Mef; @@ -97,7 +96,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.CodeAction request) return codeAction; } - private static CodeActionResolveData GetCodeActionResolveData(LSP.CodeAction request) + internal static CodeActionResolveData GetCodeActionResolveData(LSP.CodeAction request) { var resolveData = JsonSerializer.Deserialize((JsonElement)request.Data!, ProtocolConversions.LspJsonSerializerOptions); Contract.ThrowIfNull(resolveData, "Missing data for code action resolve request"); diff --git a/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHelper.cs b/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHelper.cs index cec1f54f679ab..88b7247a1cd16 100644 --- a/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHelper.cs +++ b/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHelper.cs @@ -20,11 +20,22 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions { internal class CodeActionResolveHelper { - public static async Task GetCodeActionResolveEditsAsync(RequestContext context, CodeActionResolveData data, ImmutableArray operations, CancellationToken cancellationToken) + public static Task GetCodeActionResolveEditsAsync(RequestContext context, CodeActionResolveData data, ImmutableArray operations, CancellationToken cancellationToken) { var solution = context.Solution; Contract.ThrowIfNull(solution); + return GetCodeActionResolveEditsAsync( + solution, + data, + operations, + context.GetRequiredClientCapabilities().Workspace?.WorkspaceEdit?.ResourceOperations ?? [], + context.TraceInformation, + cancellationToken); + } + + public static async Task GetCodeActionResolveEditsAsync(Solution solution, CodeActionResolveData data, ImmutableArray operations, ResourceOperationKind[] resourceOperations, Action logFunction, CancellationToken cancellationToken) + { // TO-DO: We currently must execute code actions which add new documents on the server as commands, // since there is no LSP support for adding documents yet. In the future, we should move these actions // to execute on the client. @@ -45,7 +56,7 @@ internal class CodeActionResolveHelper // only apply the portions of their work that updates documents, and nothing else. if (option is not ApplyChangesOperation applyChangesOperation) { - context.TraceInformation($"Skipping code action operation for '{data.UniqueIdentifier}'. It was a '{option.GetType().FullName}'"); + logFunction($"Skipping code action operation for '{data.UniqueIdentifier}'. It was a '{option.GetType().FullName}'"); continue; } @@ -79,8 +90,7 @@ internal class CodeActionResolveHelper || projectChange.GetRemovedAdditionalDocuments().Any() || projectChange.GetRemovedAnalyzerConfigDocuments().Any()) { - if (context.GetRequiredClientCapabilities() is not { Workspace.WorkspaceEdit.ResourceOperations: { } resourceOperations } - || !resourceOperations.Contains(ResourceOperationKind.Delete)) + if (!resourceOperations.Contains(ResourceOperationKind.Delete)) { // Removing documents is not supported by this workspace return new LSP.WorkspaceEdit { DocumentChanges = Array.Empty() }; @@ -91,8 +101,7 @@ internal class CodeActionResolveHelper || projectChange.GetAddedAdditionalDocuments().Any() || projectChange.GetAddedAnalyzerConfigDocuments().Any()) { - if (context.GetRequiredClientCapabilities() is not { Workspace.WorkspaceEdit.ResourceOperations: { } resourceOperations } - || !resourceOperations.Contains(ResourceOperationKind.Create)) + if (!resourceOperations.Contains(ResourceOperationKind.Create)) { // Adding documents is not supported by this workspace return new LSP.WorkspaceEdit { DocumentChanges = Array.Empty() }; @@ -103,8 +112,7 @@ internal class CodeActionResolveHelper || projectChange.GetChangedAdditionalDocuments().Any(docId => HasDocumentNameChange(docId, newSolution, solution) || projectChange.GetChangedAnalyzerConfigDocuments().Any(docId => HasDocumentNameChange(docId, newSolution, solution)))) { - if (context.GetRequiredClientCapabilities() is not { Workspace.WorkspaceEdit.ResourceOperations: { } resourceOperations } - || !resourceOperations.Contains(ResourceOperationKind.Rename)) + if (!resourceOperations.Contains(ResourceOperationKind.Rename)) { // Rename documents is not supported by this workspace return new LSP.WorkspaceEdit { DocumentChanges = Array.Empty() }; diff --git a/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs b/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs index a36bc0f6d91e9..0064c37cc88ba 100644 --- a/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs +++ b/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs @@ -2,29 +2,31 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Composition; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeLens; -using Roslyn.Utilities; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; -using LSP = Roslyn.LanguageServer.Protocol; -using System.Text.Json; +using Roslyn.Utilities; using StreamJsonRpc; +using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; +[ExportCSharpVisualBasicStatelessLspService(typeof(CodeLensResolveHandler)), Shared] [Method(LSP.Methods.CodeLensResolveName)] -internal sealed class CodeLensResolveHandler : ILspServiceDocumentRequestHandler +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CodeLensResolveHandler() : ILspServiceDocumentRequestHandler { /// /// Command name implemented by the client and invoked when the references code lens is selected. /// private const string ClientReferencesCommand = "roslyn.client.peekReferences"; - public CodeLensResolveHandler() - { - } - public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; diff --git a/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandlerFactory.cs b/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandlerFactory.cs deleted file mode 100644 index f067e3aa69bf3..0000000000000 --- a/src/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandlerFactory.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Composition; -using Microsoft.CodeAnalysis.Host.Mef; - -namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; - -[ExportCSharpVisualBasicLspServiceFactory(typeof(CodeLensResolveHandler)), Shared] -internal sealed class CodeLensResolveHandlerFactory : ILspServiceFactory -{ - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CodeLensResolveHandlerFactory() - { - } - - public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) - { - return new CodeLensResolveHandler(); - } -} - diff --git a/src/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs b/src/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs index 9ad91dcbe6864..050bfd25f152e 100644 --- a/src/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs +++ b/src/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs @@ -3,8 +3,10 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -99,16 +101,16 @@ public CompletionHandler( if (completionListResult == null) return null; - var (list, isIncomplete, resultId) = completionListResult.Value; + var (list, isIncomplete, isHardSelection, resultId) = completionListResult.Value; var result = await CompletionResultFactory - .ConvertToLspCompletionListAsync(document, capabilityHelper, list, isIncomplete, resultId, cancellationToken) + .ConvertToLspCompletionListAsync(document, capabilityHelper, list, isIncomplete, isHardSelection, resultId, cancellationToken) .ConfigureAwait(false); return result; } - private static async Task<(CompletionList CompletionList, bool IsIncomplete, long ResultId)?> GetFilteredCompletionListAsync( + private static async Task<(CompletionList CompletionList, bool IsIncomplete, bool isHardSelection, long ResultId)?> GetFilteredCompletionListAsync( LSP.CompletionContext? context, Document document, SourceText sourceText, @@ -157,9 +159,9 @@ public CompletionHandler( completionList = completionList.WithSpan(defaultSpan); } - var (filteredCompletionList, isIncomplete) = FilterCompletionList(completionList, completionListMaxSize, completionTrigger, sourceText); + var (filteredCompletionList, isIncomplete, isHardSelection) = FilterCompletionList(completionList, completionListMaxSize, completionTrigger, sourceText, capabilityHelper); - return (filteredCompletionList, isIncomplete, resultId); + return (filteredCompletionList, isIncomplete, isHardSelection, resultId); } private static async Task<(CompletionList CompletionList, long ResultId)?> CalculateListAsync( @@ -184,18 +186,31 @@ public CompletionHandler( return (completionList, resultId); } - private static (CompletionList CompletionList, bool IsIncomplete) FilterCompletionList( + private static (CompletionList CompletionList, bool IsIncomplete, bool isHardSelection) FilterCompletionList( CompletionList completionList, int completionListMaxSize, CompletionTrigger completionTrigger, - SourceText sourceText) + SourceText sourceText, + CompletionCapabilityHelper completionCapabilityHelper) { - if (completionListMaxSize < 0 || completionListMaxSize >= completionList.ItemsList.Count) - return (completionList, false); - var filterText = sourceText.GetSubText(completionList.Span).ToString(); var filterReason = GetFilterReason(completionTrigger); + // Determine if the list should be hard selected or soft selected. + var isFilterTextAllPunctuation = CompletionService.IsAllPunctuation(filterText); + + // If we only had punctuation - we set soft selection and the list to be incomplete so we get called back when the user continues typing. + // If they type something that is not punctuation, we may need to update the hard vs soft selection. + // For example, typing '_' should initially be soft selection, but if the user types 'o' we should hard select '_otherVar' (if it exists). + // This isn't perfect - ideally we would make this determination every time a filter character is typed, but we do not get called back + // for typing filter characters in LSP (unless we always set isIncomplete, which is expensive). + var isHardSelection = completionList.SuggestionModeItem is null && !isFilterTextAllPunctuation; + var isIncomplete = isFilterTextAllPunctuation; + + // If our completion list hasn't hit the max size, we don't need to do anything filtering + if (completionListMaxSize < 0 || completionListMaxSize >= completionList.ItemsList.Count) + return (completionList, isIncomplete, isHardSelection); + // Use pattern matching to determine which items are most relevant out of the calculated items. using var _ = ArrayBuilder.GetInstance(out var matchResultsBuilder); var index = 0; @@ -239,9 +254,11 @@ private static (CompletionList CompletionList, bool IsIncomplete) FilterCompleti // Currently the VS client does not remember to re-request, so the completion list only ever shows items from "Som" // so we always set the isIncomplete flag to true when the original list size (computed when no filter text was typed) is too large. // VS bug here - https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1335142 - var isIncomplete = completionList.ItemsList.Count > newCompletionList.ItemsList.Count; + isIncomplete |= completionCapabilityHelper.SupportVSInternalClientCapabilities + ? completionList.ItemsList.Count > newCompletionList.ItemsList.Count + : matchResultsBuilder.Count > filteredList.Length; - return (newCompletionList, isIncomplete); + return (newCompletionList, isIncomplete, isHardSelection); static CompletionFilterReason GetFilterReason(CompletionTrigger trigger) { diff --git a/src/LanguageServer/Protocol/Handler/Completion/CompletionResultFactory.cs b/src/LanguageServer/Protocol/Handler/Completion/CompletionResultFactory.cs index a74e297641ab0..a01923c0fe5f9 100644 --- a/src/LanguageServer/Protocol/Handler/Completion/CompletionResultFactory.cs +++ b/src/LanguageServer/Protocol/Handler/Completion/CompletionResultFactory.cs @@ -38,10 +38,11 @@ internal static class CompletionResultFactory CompletionCapabilityHelper capabilityHelper, CompletionList list, bool isIncomplete, + bool isHardSelection, long resultId, CancellationToken cancellationToken) { - var isSuggestionMode = list.SuggestionModeItem is not null; + var isSuggestionMode = !isHardSelection; if (list.ItemsList.Count == 0) { return new LSP.VSInternalCompletionList @@ -92,7 +93,7 @@ internal static class CompletionResultFactory // // If we have a suggestion mode item, we just need to keep the list in suggestion mode. // We don't need to return the fake suggestion mode item. - SuggestionMode = list.SuggestionModeItem != null, + SuggestionMode = isSuggestionMode, Data = capabilityHelper.SupportVSInternalCompletionListData ? resolveData : null, }; diff --git a/src/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs b/src/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs index 2ff02c450461d..1e20de1c0703c 100644 --- a/src/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs +++ b/src/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs @@ -35,7 +35,7 @@ public AbstractGoToDefinitionHandler(IMetadataAsSourceFileService metadataAsSour public abstract Task HandleRequestAsync(TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken); - protected Task GetDefinitionAsync(LSP.TextDocumentPositionParams request, bool typeOnly, RequestContext context, CancellationToken cancellationToken) + protected Task GetDefinitionAsync(LSP.TextDocumentPositionParams request, bool forSymbolType, RequestContext context, CancellationToken cancellationToken) { var workspace = context.Workspace; var document = context.Document; @@ -44,10 +44,10 @@ public AbstractGoToDefinitionHandler(IMetadataAsSourceFileService metadataAsSour var linePosition = ProtocolConversions.PositionToLinePosition(request.Position); - return GetDefinitionsAsync(_globalOptions, _metadataAsSourceFileService, workspace, document, typeOnly, linePosition, cancellationToken); + return GetDefinitionsAsync(_globalOptions, _metadataAsSourceFileService, workspace, document, forSymbolType, linePosition, cancellationToken); } - internal static async Task GetDefinitionsAsync(IGlobalOptionService globalOptions, IMetadataAsSourceFileService? metadataAsSourceFileService, Workspace workspace, Document document, bool typeOnly, LinePosition linePosition, CancellationToken cancellationToken) + internal static async Task GetDefinitionsAsync(IGlobalOptionService globalOptions, IMetadataAsSourceFileService? metadataAsSourceFileService, Workspace workspace, Document document, bool forSymbolType, LinePosition linePosition, CancellationToken cancellationToken) { var locations = ArrayBuilder.GetInstance(); var position = await document.GetPositionFromLinePositionAsync(linePosition, cancellationToken).ConfigureAwait(false); @@ -56,12 +56,12 @@ public AbstractGoToDefinitionHandler(IMetadataAsSourceFileService metadataAsSour if (service is null) return null; - var definitions = await service.GetNavigableItemsAsync(document, position, cancellationToken).ConfigureAwait(false); + var definitions = await service.GetNavigableItemsAsync(document, position, forSymbolType, cancellationToken).ConfigureAwait(false); if (definitions.Length > 0) { foreach (var definition in definitions) { - if (!ShouldInclude(definition, typeOnly)) + if (!ShouldInclude(definition, forSymbolType)) continue; var location = await ProtocolConversions.TextSpanToLocationAsync( @@ -76,27 +76,27 @@ await definition.Document.GetRequiredDocumentAsync(document.Project.Solution, ca { // No definition found - see if we can get metadata as source but that's only applicable for C#\VB. var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken).ConfigureAwait(false); + if (forSymbolType) + symbol = symbol?.GetSymbolType(); + if (symbol != null && metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol)) { - if (!typeOnly || symbol is ITypeSymbol) + var options = globalOptions.GetMetadataAsSourceOptions(); + var declarationFile = await metadataAsSourceFileService.GetGeneratedFileAsync(workspace, document.Project, symbol, signaturesOnly: false, options: options, cancellationToken: cancellationToken).ConfigureAwait(false); + + var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span; + locations.Add(new LSP.Location { - var options = globalOptions.GetMetadataAsSourceOptions(); - var declarationFile = await metadataAsSourceFileService.GetGeneratedFileAsync(workspace, document.Project, symbol, signaturesOnly: false, options: options, cancellationToken: cancellationToken).ConfigureAwait(false); - - var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span; - locations.Add(new LSP.Location - { - Uri = ProtocolConversions.CreateAbsoluteUri(declarationFile.FilePath), - Range = ProtocolConversions.LinePositionToRange(linePosSpan), - }); - } + Uri = ProtocolConversions.CreateAbsoluteUri(declarationFile.FilePath), + Range = ProtocolConversions.LinePositionToRange(linePosSpan), + }); } } return locations.ToArrayAndFree(); // local functions - static bool ShouldInclude(INavigableItem item, bool typeOnly) + static bool ShouldInclude(INavigableItem item, bool forSymbolType) { if (item.Glyph is Glyph.Namespace) { @@ -104,7 +104,7 @@ static bool ShouldInclude(INavigableItem item, bool typeOnly) return false; } - if (!typeOnly) + if (!forSymbolType) { return true; } diff --git a/src/LanguageServer/Protocol/Handler/Definitions/GoToDefinitionHandler.cs b/src/LanguageServer/Protocol/Handler/Definitions/GoToDefinitionHandler.cs index b25d426dd96ff..a8396ff8fdd5b 100644 --- a/src/LanguageServer/Protocol/Handler/Definitions/GoToDefinitionHandler.cs +++ b/src/LanguageServer/Protocol/Handler/Definitions/GoToDefinitionHandler.cs @@ -25,6 +25,6 @@ public GoToDefinitionHandler(IMetadataAsSourceFileService metadataAsSourceFileSe } public override Task HandleRequestAsync(LSP.TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) - => GetDefinitionAsync(request, typeOnly: false, context, cancellationToken); + => GetDefinitionAsync(request, forSymbolType: false, context, cancellationToken); } } diff --git a/src/LanguageServer/Protocol/Handler/Definitions/GoToTypeDefinitionHandler.cs b/src/LanguageServer/Protocol/Handler/Definitions/GoToTypeDefinitionHandler.cs index 969b512be3f27..11c23a9751d9c 100644 --- a/src/LanguageServer/Protocol/Handler/Definitions/GoToTypeDefinitionHandler.cs +++ b/src/LanguageServer/Protocol/Handler/Definitions/GoToTypeDefinitionHandler.cs @@ -25,6 +25,6 @@ public GoToTypeDefinitionHandler(IMetadataAsSourceFileService metadataAsSourceFi } public override Task HandleRequestAsync(LSP.TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) - => GetDefinitionAsync(request, typeOnly: true, context, cancellationToken); + => GetDefinitionAsync(request, forSymbolType: true, context, cancellationToken); } } diff --git a/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticsPullCache.cs b/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticsPullCache.cs index 41e00efaeae52..2d1b001f6f095 100644 --- a/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticsPullCache.cs +++ b/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticsPullCache.cs @@ -24,7 +24,7 @@ internal record struct DiagnosticsRequestState(Project Project, int GlobalStateV /// and works well for us in the normal case. The latter still allows us to reuse diagnostics when changes happen that /// update the version stamp but not the content (for example, forking LSP text). /// - private sealed class DiagnosticsPullCache(string uniqueKey) : VersionedPullCache<(int globalStateVersion, VersionStamp? dependentVersion), (int globalStateVersion, Checksum dependentChecksum), DiagnosticsRequestState, DiagnosticData>(uniqueKey) + private sealed class DiagnosticsPullCache(string uniqueKey) : VersionedPullCache<(int globalStateVersion, VersionStamp? dependentVersion), (int globalStateVersion, Checksum dependentChecksum), DiagnosticsRequestState, ImmutableArray>(uniqueKey) { public override async Task<(int globalStateVersion, VersionStamp? dependentVersion)> ComputeCheapVersionAsync(DiagnosticsRequestState state, CancellationToken cancellationToken) { diff --git a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs index c15f24183032d..037224a028503 100644 --- a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs +++ b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs @@ -17,5 +17,5 @@ public InlayHintCache() : base(maxCacheSize: 3) /// /// Cached data need to resolve a specific inlay hint item. /// - internal record InlayHintCacheEntry(ImmutableArray InlayHintMembers, VersionStamp SyntaxVersion); + internal record InlayHintCacheEntry(ImmutableArray InlayHintMembers); } diff --git a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs index 8ce98a031b551..3822fa6400adb 100644 --- a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs +++ b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs @@ -50,16 +50,12 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(InlayHintParams request) internal static async Task GetInlayHintsAsync(Document document, TextDocumentIdentifier textDocumentIdentifier, LSP.Range range, InlineHintsOptions options, bool displayAllOverride, InlayHintCache inlayHintCache, CancellationToken cancellationToken) { var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - var textSpan = ProtocolConversions.RangeToTextSpan(range, text); - - var inlineHintService = document.GetRequiredLanguageService(); - var hints = await inlineHintService.GetInlineHintsAsync(document, textSpan, options, displayAllOverride, cancellationToken).ConfigureAwait(false); - + var hints = await CalculateInlayHintsAsync(document, range, options, displayAllOverride, cancellationToken).ConfigureAwait(false); var syntaxVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false); // Store the members in the resolve cache so that when we get a resolve request for a particular // member we can re-use the inline hint. - var resultId = inlayHintCache.UpdateCache(new InlayHintCache.InlayHintCacheEntry(hints, syntaxVersion)); + var resultId = inlayHintCache.UpdateCache(new InlayHintCache.InlayHintCacheEntry(hints)); var inlayHints = new LSP.InlayHint[hints.Length]; for (var i = 0; i < hints.Length; i++) @@ -89,7 +85,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(InlayHintParams request) ToolTip = null, PaddingLeft = leftPadding, PaddingRight = rightPadding, - Data = new InlayHintResolveData(resultId, i, textDocumentIdentifier) + Data = new InlayHintResolveData(resultId, i, textDocumentIdentifier, syntaxVersion.ToString(), range, displayAllOverride) }; inlayHints[i] = inlayHint; @@ -98,6 +94,16 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(InlayHintParams request) return inlayHints; } + internal static async Task> CalculateInlayHintsAsync(Document document, LSP.Range range, InlineHintsOptions options, bool displayAllOverride, CancellationToken cancellationToken) + { + var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + var textSpan = ProtocolConversions.RangeToTextSpan(range, text); + + var inlineHintService = document.GetRequiredLanguageService(); + var hints = await inlineHintService.GetInlineHintsAsync(document, textSpan, options, displayAllOverride, cancellationToken).ConfigureAwait(false); + return hints; + } + /// /// Goes through the tagged text of the hint and trims off leading and trailing spaces. /// If there is leading or trailing space, then we want to add padding to the left and right accordingly. diff --git a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveData.cs b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveData.cs index 0024e8b8e3d18..37b4504ef9440 100644 --- a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveData.cs +++ b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveData.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.CodeAnalysis.Text; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint; @@ -12,4 +13,4 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint; /// the resultId associated with the inlay hint created on original request. /// the index of the specific inlay hint item in the original list. /// the text document associated with the inlay hint to resolve. -internal sealed record InlayHintResolveData(long ResultId, int ListIndex, TextDocumentIdentifier TextDocument) : DocumentResolveData(TextDocument); +internal sealed record InlayHintResolveData(long ResultId, int ListIndex, TextDocumentIdentifier TextDocument, string SyntaxVersion, Range Range, bool DisplayAllOverride) : DocumentResolveData(TextDocument); diff --git a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs index 468e908a507e6..08cc5c33bd95e 100644 --- a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs +++ b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs @@ -2,10 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Composition; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.InlineHints; +using Microsoft.CodeAnalysis.Options; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; using StreamJsonRpc; @@ -13,16 +17,12 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint { + [ExportCSharpVisualBasicStatelessLspService(typeof(InlayHintResolveHandler)), Shared] [Method(Methods.InlayHintResolveName)] - internal sealed class InlayHintResolveHandler : ILspServiceDocumentRequestHandler + [method: ImportingConstructor] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + internal sealed class InlayHintResolveHandler(IGlobalOptionService globalOptions) : ILspServiceDocumentRequestHandler { - private readonly InlayHintCache _inlayHintCache; - - public InlayHintResolveHandler(InlayHintCache inlayHintCache) - { - _inlayHintCache = inlayHintCache; - } - public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; @@ -33,39 +33,53 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.InlayHint request) public Task HandleRequestAsync(LSP.InlayHint request, RequestContext context, CancellationToken cancellationToken) { var document = context.GetRequiredDocument(); - return ResolveInlayHintAsync(document, request, _inlayHintCache, cancellationToken); + var options = globalOptions.GetInlineHintsOptions(document.Project.Language); + var inlayHintCache = context.GetRequiredService(); + var resolveData = GetInlayHintResolveData(request); + return ResolveInlayHintAsync(document, request, inlayHintCache, resolveData, options, cancellationToken); } - internal static async Task ResolveInlayHintAsync(Document document, LSP.InlayHint request, InlayHintCache inlayHintCache, CancellationToken cancellationToken) + internal static async Task ResolveInlayHintAsync( + Document document, + LSP.InlayHint request, + InlayHintCache inlayHintCache, + InlayHintResolveData resolveData, + InlineHintsOptions options, + CancellationToken cancellationToken) { - var resolveData = GetInlayHintResolveData(request); - var (cacheEntry, inlineHintToResolve) = GetCacheEntry(resolveData, inlayHintCache); - var currentSyntaxVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false); - var cachedSyntaxVersion = cacheEntry.SyntaxVersion; + var resolveSyntaxVersion = resolveData.SyntaxVersion; - if (currentSyntaxVersion != cachedSyntaxVersion) + if (currentSyntaxVersion.ToString() != resolveSyntaxVersion) { - throw new LocalRpcException($"Cached resolve version {cachedSyntaxVersion} does not match current version {currentSyntaxVersion}") + throw new LocalRpcException($"Request resolve version {resolveSyntaxVersion} does not match current version {currentSyntaxVersion}") { ErrorCode = LspErrorCodes.ContentModified }; } - var taggedText = await inlineHintToResolve.GetDescriptionAsync(document, cancellationToken).ConfigureAwait(false); + var inlineHintToResolve = GetCacheEntry(resolveData, inlayHintCache); + if (inlineHintToResolve is null) + { + // It is very possible that the cache no longer contains the hint being resolved (for example, multiple documents open side by side). + // Instead of throwing, we can recompute the hints since we've already verified above that the version has not changed. + var hints = await InlayHintHandler.CalculateInlayHintsAsync(document, resolveData.Range, options, resolveData.DisplayAllOverride, cancellationToken).ConfigureAwait(false); + inlineHintToResolve = hints[resolveData.ListIndex]; + } + + var taggedText = await inlineHintToResolve.Value.GetDescriptionAsync(document, cancellationToken).ConfigureAwait(false); request.ToolTip = ProtocolConversions.GetDocumentationMarkupContent(taggedText, document, true); return request; } - private static (InlayHintCache.InlayHintCacheEntry CacheEntry, InlineHint InlineHintToResolve) GetCacheEntry(InlayHintResolveData resolveData, InlayHintCache inlayHintCache) + private static InlineHint? GetCacheEntry(InlayHintResolveData resolveData, InlayHintCache inlayHintCache) { var cacheEntry = inlayHintCache.GetCachedEntry(resolveData.ResultId); - Contract.ThrowIfNull(cacheEntry, "Missing cache entry for inlay hint resolve request"); - return (cacheEntry, cacheEntry.InlayHintMembers[resolveData.ListIndex]); + return cacheEntry?.InlayHintMembers[resolveData.ListIndex]; } - private static InlayHintResolveData GetInlayHintResolveData(LSP.InlayHint inlayHint) + internal static InlayHintResolveData GetInlayHintResolveData(LSP.InlayHint inlayHint) { Contract.ThrowIfNull(inlayHint.Data); var resolveData = JsonSerializer.Deserialize((JsonElement)inlayHint.Data, ProtocolConversions.LspJsonSerializerOptions); diff --git a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandlerFactory.cs b/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandlerFactory.cs deleted file mode 100644 index 1ef5fb2048c6e..0000000000000 --- a/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandlerFactory.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Composition; -using Microsoft.CodeAnalysis.Host.Mef; - -namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint -{ - [ExportCSharpVisualBasicLspServiceFactory(typeof(InlayHintResolveHandler)), Shared] - internal sealed class InlayHintResolveHandlerFactory : ILspServiceFactory - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public InlayHintResolveHandlerFactory() - { - } - - public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) - { - var inlayHintCache = lspServices.GetRequiredService(); - return new InlayHintResolveHandler(inlayHintCache); - } - } -} diff --git a/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs b/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs index 0e8cfb9fe6f41..cc6c5cd737189 100644 --- a/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs +++ b/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs @@ -114,19 +114,17 @@ internal sealed class OnAutoInsertHandler( DocumentationCommentOptions options, CancellationToken cancellationToken) { - var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - var sourceText = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + var parsedDocument = await ParsedDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); + var sourceText = parsedDocument.Text; var position = sourceText.Lines.GetPosition(linePosition); var result = character == "\n" - ? service.GetDocumentationCommentSnippetOnEnterTyped(syntaxTree, sourceText, position, options, cancellationToken) - : service.GetDocumentationCommentSnippetOnCharacterTyped(syntaxTree, sourceText, position, options, cancellationToken, addIndentation: false); + ? service.GetDocumentationCommentSnippetOnEnterTyped(parsedDocument, position, options, cancellationToken) + : service.GetDocumentationCommentSnippetOnCharacterTyped(parsedDocument, position, options, cancellationToken, addIndentation: false); if (result == null) - { return null; - } return new LSP.VSInternalDocumentOnAutoInsertResponseItem { diff --git a/src/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache.CacheItem.cs b/src/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache.CacheItem.cs index 4c7abb5d4ba86..6def0f52d7f81 100644 --- a/src/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache.CacheItem.cs +++ b/src/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache.CacheItem.cs @@ -52,7 +52,7 @@ private sealed class CacheItem(string uniqueKey) /// /// Returns if the previousPullResult can be re-used, otherwise returns a new resultId and the new data associated with it. /// - public async Task<(string, ImmutableArray)?> UpdateCacheItemAsync( + public async Task<(string, TComputedData)?> UpdateCacheItemAsync( VersionedPullCache cache, PreviousPullResult? previousPullResult, bool isFullyLoaded, diff --git a/src/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache.cs b/src/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache.cs index 740ee258b2a3c..aacaeca1cdc14 100644 --- a/src/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache.cs +++ b/src/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache.cs @@ -4,7 +4,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; -using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; @@ -19,7 +18,6 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler; /// with different computation costs to determine if the previous cached data is still valid. /// internal abstract partial class VersionedPullCache(string uniqueKey) - where TComputedData : notnull { /// /// Map of workspace and diagnostic source to the data used to make the last pull report. @@ -59,9 +57,9 @@ internal abstract partial class VersionedPullCache. /// - public abstract Task> ComputeDataAsync(TState state, CancellationToken cancellationToken); + public abstract Task ComputeDataAsync(TState state, CancellationToken cancellationToken); - public abstract Checksum ComputeChecksum(ImmutableArray data); + public abstract Checksum ComputeChecksum(TComputedData data); /// /// If results have changed since the last request this calculates and returns a new @@ -70,7 +68,7 @@ internal abstract partial class VersionedPullCachea map of roslyn document or project id to the previous result the client sent us for that doc. /// the id of the project or document that we are checking to see if it has changed. /// Null when results are unchanged, otherwise returns a non-null new resultId. - public async Task<(string ResultId, ImmutableArray Data)?> GetOrComputeNewDataAsync( + public async Task<(string ResultId, TComputedData Data)?> GetOrComputeNewDataAsync( Dictionary idToClientLastResult, ProjectOrDocumentId projectOrDocumentId, Project project, diff --git a/src/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs b/src/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs index 9328c140b3804..eb6ae129e6aff 100644 --- a/src/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs +++ b/src/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs @@ -216,6 +216,7 @@ public override async ValueTask OnReferencesFoundAsync(IAsyncEnumerable(uniqueKey), ILspService +{ + public override async Task<(SourceGeneratorExecutionVersion, VersionStamp)> ComputeCheapVersionAsync(SourceGeneratedDocumentGetTextState state, CancellationToken cancellationToken) + { + // The execution version and the dependent version must be considered as one version cached together - + // it is not correct to say that if the execution version is the same then we can re-use results (as in automatic mode the execution version never changes). + var executionVersion = state.Document.Project.Solution.GetSourceGeneratorExecutionVersion(state.Document.Project.Id); + var dependentVersion = await state.Document.Project.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false); + return (executionVersion, dependentVersion); + } + + public override Task ComputeExpensiveVersionAsync(SourceGeneratedDocumentGetTextState state, CancellationToken cancellationToken) + { + return SpecializedTasks.Null(); + } + + public override Checksum ComputeChecksum(SourceText? data) + { + return data is null ? Checksum.Null : Checksum.From(data.GetChecksum()); + } + + public override async Task ComputeDataAsync(SourceGeneratedDocumentGetTextState state, CancellationToken cancellationToken) + { + // When a user has a open source-generated file, we ensure that the contents in the LSP snapshot match the contents that we + // get through didOpen/didChanges, like any other file. That way operations in LSP file are in sync with the + // contents the user has. However in this case, we don't want to look at that frozen text, but look at what the + // generator would generate if we ran it again. Otherwise, we'll get "stuck" and never update the file with something new. + // This can return null when the source generated file has been removed (but the queue itself is using the frozen non-null document). + var unfrozenDocument = await state.Document.Project.Solution.WithoutFrozenSourceGeneratedDocuments().GetDocumentAsync(state.Document.Id, includeSourceGenerated: true, cancellationToken).ConfigureAwait(false); + return unfrozenDocument == null + ? null + : await unfrozenDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + } +} + +[ExportCSharpVisualBasicLspServiceFactory(typeof(SourceGeneratedDocumentCache)), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class SourceGeneratedDocumentCacheFactory() : ILspServiceFactory +{ + public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) + { + return new SourceGeneratedDocumentCache(this.GetType().Name); + } +} diff --git a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentGetTextHandler.cs b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentGetTextHandler.cs index 31402aaa61f25..6354c26e9b464 100644 --- a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentGetTextHandler.cs +++ b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentGetTextHandler.cs @@ -3,14 +3,17 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; -namespace Microsoft.CodeAnalysis.LanguageServer.Handler; +namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SourceGenerators; [ExportCSharpVisualBasicStatelessLspService(typeof(SourceGeneratedDocumentGetTextHandler)), Shared] [Method(MethodName)] @@ -29,18 +32,46 @@ public async Task HandleRequestAsync(SourceGenerato { var document = context.Document; + if (document is null) + { + // The source generated file being asked about is not present. + // This is a rare case the request queue always gives us a frozen, non-null document for any opened sg document, + // even if the generator itself was removed and the document no longer exists in the host solution. + // + // We can only get a null document here if the sg document has not been opened and + // the source generated document does not exist in the workspace. + // + // Return a value indicating that the document is removed. + return new SourceGeneratedDocumentText(ResultId: null, Text: null); + } + // Nothing here strictly prevents this from working on any other document, but we'll assert we got a source-generated file, since // it wouldn't really make sense for the server to be asked for the contents of a regular file. Since this endpoint is intended for // source-generated files only, this would indicate that something else has gone wrong. Contract.ThrowIfFalse(document is SourceGeneratedDocument); - // When a user has a open source-generated file, we ensure that the contents in the LSP snapshot match the contents that we - // get through didOpen/didChanges, like any other file. That way operations in LSP file are in sync with the - // contents the user has. However in this case, we don't want to look at that frozen text, but look at what the - // generator would generate if we ran it again. Otherwise, we'll get "stuck" and never update the file with something new. - document = await document.Project.Solution.WithoutFrozenSourceGeneratedDocuments().GetDocumentAsync(document.Id, includeSourceGenerated: true, cancellationToken).ConfigureAwait(false); + var cache = context.GetRequiredLspService(); + var projectOrDocument = new ProjectOrDocumentId(document.Id); + + using var _ = PooledDictionary.GetInstance(out var previousPullResults); + if (request.ResultId is not null) + { + previousPullResults.Add(projectOrDocument, new PreviousPullResult(request.ResultId, request.TextDocument)); + } + + var newResult = await cache.GetOrComputeNewDataAsync(previousPullResults, projectOrDocument, document.Project, new SourceGeneratedDocumentGetTextState(document), cancellationToken).ConfigureAwait(false); - var text = document != null ? await document.GetTextAsync(cancellationToken).ConfigureAwait(false) : null; - return new SourceGeneratedDocumentText(text?.ToString()); + if (newResult is null) + { + Contract.ThrowIfNull(request.ResultId, "Attempted to reuse cache entry but given no resultId"); + // The generated document is the same, we can return the same resultId. + return new SourceGeneratedDocumentText(request.ResultId, Text: null); + } + else + { + // We may get no text back if the unfrozen source generated file no longer exists. + var data = newResult.Value.Data?.ToString(); + return new SourceGeneratedDocumentText(newResult.Value.ResultId, data); + } } } diff --git a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentText.cs b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentText.cs index 17c1105a0870e..10fc9991c6c93 100644 --- a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentText.cs +++ b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratedDocumentText.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -6,4 +6,16 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler; -internal sealed record SourceGeneratedDocumentText([property: JsonPropertyName("text")] string? Text); \ No newline at end of file +/// +/// Source generated file text result. The client uses the resultId to inform what the text value is. +/// +/// An unchanged result has a non-null resultId (same as client request resultId) + null text. +/// +/// A changed result has a new non-null resultId + possibly null text (if the sg document no longer exists). +/// +/// In rare circumstances it is possible to get a null resultId + null text - this happens when +/// the source generated document is not open AND the source generated document no longer exists +/// +internal sealed record SourceGeneratedDocumentText( + [property: JsonPropertyName("resultId")] string? ResultId, + [property: JsonPropertyName("text")] string? Text); diff --git a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorGetTextParams.cs b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorGetTextParams.cs index f930f1cc438c0..9537b6d607d2b 100644 --- a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorGetTextParams.cs +++ b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorGetTextParams.cs @@ -1,10 +1,12 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; -namespace Microsoft.CodeAnalysis.LanguageServer.Handler; +namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SourceGenerators; -internal sealed record SourceGeneratorGetTextParams([property: JsonPropertyName("textDocument")] TextDocumentIdentifier TextDocument) : ITextDocumentParams; \ No newline at end of file +internal sealed record SourceGeneratorGetTextParams( + [property: JsonPropertyName("textDocument")] TextDocumentIdentifier TextDocument, + [property: JsonPropertyName("resultId")] string? ResultId) : ITextDocumentParams; diff --git a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueue.cs b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueue.cs new file mode 100644 index 0000000000000..e88c75b25ae9c --- /dev/null +++ b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueue.cs @@ -0,0 +1,147 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Roslyn.LanguageServer.Protocol; +using Roslyn.Utilities; +using StreamJsonRpc; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SourceGenerators; + +internal sealed class SourceGeneratorRefreshQueue : + IOnInitialized, + ILspService, + IDisposable +{ + private const string RefreshSourceGeneratedDocumentName = "workspace/refreshSourceGeneratedDocument"; + + private readonly IAsynchronousOperationListener _asyncListener; + private readonly CancellationTokenSource _disposalTokenSource = new(); + private readonly LspWorkspaceRegistrationService _lspWorkspaceRegistrationService; + private readonly LspWorkspaceManager _lspWorkspaceManager; + private readonly IClientLanguageServerManager _notificationManager; + private readonly AsyncBatchingWorkQueue _refreshQueue; + + public SourceGeneratorRefreshQueue( + IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider, + LspWorkspaceRegistrationService lspWorkspaceRegistrationService, + LspWorkspaceManager lspWorkspaceManager, + IClientLanguageServerManager notificationManager) + { + _lspWorkspaceRegistrationService = lspWorkspaceRegistrationService; + _lspWorkspaceManager = lspWorkspaceManager; + _notificationManager = notificationManager; + _asyncListener = asynchronousOperationListenerProvider.GetListener(FeatureAttribute.SourceGenerators); + + // Batch up workspace notifications so that we only send a notification to refresh source generated files + // every 2 seconds - long enough to avoid spamming the client with notifications, but short enough to refresh + // the source generated files relatively frequently. + _refreshQueue = _refreshQueue = new AsyncBatchingWorkQueue( + delay: DelayTimeSpan.Idle, + processBatchAsync: RefreshSourceGeneratedDocumentsAsync, + asyncListener: _asyncListener, + _disposalTokenSource.Token); + } + + public Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestContext context, CancellationToken cancellationToken) + { + if (clientCapabilities.HasVisualStudioLspCapability()) + { + // VS source generated document content is not provided by LSP. + return Task.CompletedTask; + } + + // After we have initialized we can start listening for workspace changes. + _lspWorkspaceRegistrationService.LspSolutionChanged += OnLspSolutionChanged; + return Task.CompletedTask; + } + + private void OnLspSolutionChanged(object? sender, WorkspaceChangeEventArgs e) + { + var asyncToken = _asyncListener.BeginAsyncOperation($"{nameof(SourceGeneratorRefreshQueue)}.{nameof(OnLspSolutionChanged)}"); + _ = OnLspSolutionChangedAsync(e) + .CompletesAsyncOperation(asyncToken) + .ReportNonFatalErrorUnlessCancelledAsync(_disposalTokenSource.Token); + } + + private async Task OnLspSolutionChangedAsync(WorkspaceChangeEventArgs e) + { + var projectId = e.ProjectId ?? e.DocumentId?.ProjectId; + if (projectId is not null) + { + // We have a specific changed project - do some additional checks to see if + // source generators possibly changed. Note that this overreports actual + // changes to the source generated text; we rely on resultIds in the text retrieval to avoid unnecessary serialization. + + // Trivial check. see if the SG version of these projects changed. If so, we definitely want to update + // this generated file. + if (e.OldSolution.GetSourceGeneratorExecutionVersion(projectId) != + e.NewSolution.GetSourceGeneratorExecutionVersion(projectId)) + { + _refreshQueue.AddWork(); + return; + } + + var oldProject = e.OldSolution.GetProject(projectId); + var newProject = e.NewSolution.GetProject(projectId); + + if (oldProject != null && newProject != null) + { + await CheckDependentVersionsAsync(oldProject, newProject).ConfigureAwait(false); + } + } + else + { + // We don't have a specific project change - if this is a solution change we need to queue a refresh anyway. + if (e.Kind is WorkspaceChangeKind.SolutionChanged or WorkspaceChangeKind.SolutionAdded or WorkspaceChangeKind.SolutionRemoved or WorkspaceChangeKind.SolutionReloaded or WorkspaceChangeKind.SolutionCleared) + { + _refreshQueue.AddWork(); + } + } + + async Task CheckDependentVersionsAsync(Project oldProject, Project newProject) + { + if (await oldProject.GetDependentVersionAsync(_disposalTokenSource.Token).ConfigureAwait(false) != + await newProject.GetDependentVersionAsync(_disposalTokenSource.Token).ConfigureAwait(false)) + { + _refreshQueue.AddWork(); + } + } + } + + private ValueTask RefreshSourceGeneratedDocumentsAsync( + CancellationToken cancellationToken) + { + var hasOpenSourceGeneratedDocuments = _lspWorkspaceManager.GetTrackedLspText().Keys.Any(uri => uri.Scheme == SourceGeneratedDocumentUri.Scheme); + if (!hasOpenSourceGeneratedDocuments) + { + // There are no opened source generated documents - we don't need to bother asking the client to refresh anything. + return ValueTaskFactory.CompletedTask; + } + + try + { + return _notificationManager.SendNotificationAsync(RefreshSourceGeneratedDocumentName, cancellationToken); + } + catch (Exception ex) when (ex is ObjectDisposedException or ConnectionLostException) + { + // It is entirely possible that we're shutting down and the connection is lost while we're trying to send a notification + // as this runs outside of the guaranteed ordering in the queue. We can safely ignore this exception. + } + + return ValueTaskFactory.CompletedTask; + } + + public void Dispose() + { + _lspWorkspaceRegistrationService.LspSolutionChanged -= OnLspSolutionChanged; + _disposalTokenSource.Cancel(); + _disposalTokenSource.Dispose(); + } +} diff --git a/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueueFactory.cs b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueueFactory.cs new file mode 100644 index 0000000000000..4b8531d0bf5ff --- /dev/null +++ b/src/LanguageServer/Protocol/Handler/SourceGenerators/SourceGeneratorRefreshQueueFactory.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.TestHooks; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SourceGenerators; + +[ExportCSharpVisualBasicLspServiceFactory(typeof(SourceGeneratorRefreshQueue)), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class SourceGeneratorRefreshQueueFactory( + IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider, + LspWorkspaceRegistrationService lspWorkspaceRegistrationService) : ILspServiceFactory +{ + public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) + { + var notificationManager = lspServices.GetRequiredService(); + var lspWorkspaceManager = lspServices.GetRequiredService(); + return new SourceGeneratorRefreshQueue(asynchronousOperationListenerProvider, lspWorkspaceRegistrationService, lspWorkspaceManager, notificationManager); + } +} \ No newline at end of file diff --git a/src/LanguageServer/Protocol/Handler/SpellCheck/SpellCheckPullCache.cs b/src/LanguageServer/Protocol/Handler/SpellCheck/SpellCheckPullCache.cs index 33df968335b8e..d997635b6cb60 100644 --- a/src/LanguageServer/Protocol/Handler/SpellCheck/SpellCheckPullCache.cs +++ b/src/LanguageServer/Protocol/Handler/SpellCheck/SpellCheckPullCache.cs @@ -16,7 +16,7 @@ internal record struct SpellCheckState(ISpellCheckSpanService Service, Document /// Simplified version of that only uses a /// single cheap key to check results against. /// -internal sealed class SpellCheckPullCache(string uniqueKey) : VersionedPullCache<(Checksum parseOptionsChecksum, Checksum textChecksum)?, object?, SpellCheckState, SpellCheckSpan>(uniqueKey) +internal sealed class SpellCheckPullCache(string uniqueKey) : VersionedPullCache<(Checksum parseOptionsChecksum, Checksum textChecksum)?, object?, SpellCheckState, ImmutableArray>(uniqueKey) { public override async Task<(Checksum parseOptionsChecksum, Checksum textChecksum)?> ComputeCheapVersionAsync(SpellCheckState state, CancellationToken cancellationToken) { diff --git a/src/LanguageServer/Protocol/LspServices/RequestTelemetryScope.cs b/src/LanguageServer/Protocol/LspServices/RequestTelemetryScope.cs index 7cc25cf9e74ba..2408cd4025890 100644 --- a/src/LanguageServer/Protocol/LspServices/RequestTelemetryScope.cs +++ b/src/LanguageServer/Protocol/LspServices/RequestTelemetryScope.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.Utilities; @@ -29,6 +30,9 @@ public override void RecordCancellation() public override void RecordException(Exception exception) { + // Report a NFW report for the request failure, as well as recording statistics on the failure. + ReportNonFatalError(exception); + _result = RequestTelemetryLogger.Result.Failed; } @@ -43,4 +47,18 @@ public override void Dispose() _telemetryLogger.UpdateTelemetryData(Name, Language, _queuedDuration, requestDuration, _result); } + + private static void ReportNonFatalError(Exception exception) + { + if (exception is StreamJsonRpc.LocalRpcException localRpcException && localRpcException.ErrorCode == LspErrorCodes.ContentModified) + { + // We throw content modified exceptions when asked to resolve code lens / inlay hints associated with a solution version we no longer have. + // This generally happens when the project changes underneath us. The client is eventually told to refresh, + // but they can send us resolve requests for prior versions before they see the refresh. + // There is no need to report these exceptions as NFW since they are expected to occur in normal workflows. + return; + } + + FatalError.ReportAndPropagateUnlessCanceled(exception, ErrorSeverity.Critical); + } } diff --git a/src/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs b/src/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs index add3d0afb01b7..fefbd26e43797 100644 --- a/src/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs +++ b/src/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs @@ -31,7 +31,7 @@ public int Id /// Gets or sets the reference location. /// [JsonPropertyName("_vs_location")] - public Location Location + public Location? Location { get; set; diff --git a/src/LanguageServer/Protocol/RequestExecutionQueueProvider.cs b/src/LanguageServer/Protocol/RequestExecutionQueueProvider.cs index 66bbad16e4f66..7e39bbc1dbc40 100644 --- a/src/LanguageServer/Protocol/RequestExecutionQueueProvider.cs +++ b/src/LanguageServer/Protocol/RequestExecutionQueueProvider.cs @@ -6,7 +6,6 @@ using System.Composition; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CommonLanguageServerProtocol.Framework; namespace Microsoft.CodeAnalysis.LanguageServer; @@ -14,11 +13,11 @@ namespace Microsoft.CodeAnalysis.LanguageServer; [ExportCSharpVisualBasicStatelessLspService(typeof(IRequestExecutionQueueProvider)), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, true)] -internal sealed class RequestExecutionQueueProvider(IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider) : IRequestExecutionQueueProvider +internal sealed class RequestExecutionQueueProvider() : IRequestExecutionQueueProvider { public IRequestExecutionQueue CreateRequestExecutionQueue(AbstractLanguageServer languageServer, ILspLogger logger, AbstractHandlerProvider handlerProvider) { - var queue = new RoslynRequestExecutionQueue(languageServer, logger, handlerProvider, asynchronousOperationListenerProvider); + var queue = new RoslynRequestExecutionQueue(languageServer, logger, handlerProvider); queue.Start(); return queue; } diff --git a/src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs b/src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs index e12fced425f83..96d786c34f98c 100644 --- a/src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs +++ b/src/LanguageServer/Protocol/RoslynRequestExecutionQueue.cs @@ -5,66 +5,36 @@ using System; using System.Globalization; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CommonLanguageServerProtocol.Framework; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer { internal sealed class RoslynRequestExecutionQueue : RequestExecutionQueue { private readonly IInitializeManager _initializeManager; - private readonly IAsynchronousOperationListener _listener; /// /// Serial access is guaranteed by the queue. /// private CultureInfo? _cultureInfo; - public RoslynRequestExecutionQueue(AbstractLanguageServer languageServer, ILspLogger logger, AbstractHandlerProvider handlerProvider, IAsynchronousOperationListenerProvider provider) + public RoslynRequestExecutionQueue(AbstractLanguageServer languageServer, ILspLogger logger, AbstractHandlerProvider handlerProvider) : base(languageServer, logger, handlerProvider) { _initializeManager = languageServer.GetLspServices().GetRequiredService(); - _listener = provider.GetListener(FeatureAttribute.LanguageServer); } - public override async Task WrapStartRequestTaskAsync(Task nonMutatingRequestTask, bool rethrowExceptions) + public override async Task WrapStartRequestTaskAsync(Task requestTask, bool rethrowExceptions) { - using var token = _listener.BeginAsyncOperation(nameof(WrapStartRequestTaskAsync)); - if (rethrowExceptions) + try { - try - { - await nonMutatingRequestTask.ConfigureAwait(false); - } - catch (StreamJsonRpc.LocalRpcException localRpcException) when (localRpcException.ErrorCode == LspErrorCodes.ContentModified) - { - // Content modified exceptions are expected and should not be reported as NFWs. - throw; - } - // If we had an exception, we want to record a NFW for it AND propogate it out to the queue so it can be handled appropriately. - catch (Exception ex) when (FatalError.ReportAndPropagateUnlessCanceled(ex, ErrorSeverity.Critical)) - { - throw ExceptionUtilities.Unreachable(); - } + await requestTask.ConfigureAwait(false); } - else + catch (Exception) when (!rethrowExceptions) { - // The caller has asked us to not rethrow, so record a NFW and swallow. - try - { - await nonMutatingRequestTask.ConfigureAwait(false); - } - catch (StreamJsonRpc.LocalRpcException localRpcException) when (localRpcException.ErrorCode == LspErrorCodes.ContentModified) - { - // Content modified exceptions are expected and should not be reported as NFWs. - } - catch (Exception ex) when (FatalError.ReportAndCatchUnlessCanceled(ex, ErrorSeverity.Critical)) - { - // Swallow the exception so it does not bubble up into the queue. - } + // The caller has asked us to not rethrow, so swallow the exception to avoid bringing down the queue. + // The queue item task itself already handles reporting the exception (if any). } } diff --git a/src/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs b/src/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs index d3353ae4f9a35..0b68236cd9f3a 100644 --- a/src/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs @@ -809,6 +809,58 @@ public async Task TestSoftSelectionWhenFilterTextIsEmptyForPreselectItemAsync(bo Assert.Null(item.CommitCharacters); } + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/vscode-csharp/issues/7623")] + public async Task TestSoftSelectionForDiscardAsync(bool mutatingLspWorkspace) + { + var markup = +@" +public class A +{ + public void M() + { + var _someDiscard = 1; + _{|caret:|} + } +}"; + + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, DefaultClientCapabilities); + var caretLocation = testLspServer.GetLocations("caret").Single(); + await testLspServer.OpenDocumentAsync(caretLocation.Uri); + + testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(CompletionOptionsStorage.TriggerInArgumentLists, LanguageNames.CSharp, true); + + var completionParams = CreateCompletionParams( + caretLocation, + invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, + triggerCharacter: "_", + triggerKind: LSP.CompletionTriggerKind.Invoked); + + var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); + var results = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCompletionName, completionParams, CancellationToken.None).ConfigureAwait(false); + var actualItem = results.Items.First(i => i.Label == "_someDiscard"); + + Assert.True(results.IsIncomplete); + Assert.Empty(results.ItemDefaults.CommitCharacters); + Assert.Equal("_someDiscard", actualItem.Label); + Assert.Null(actualItem.CommitCharacters); + + await testLspServer.InsertTextAsync(caretLocation.Uri, (caretLocation.Range.End.Line, caretLocation.Range.End.Character, "s")); + + completionParams = CreateCompletionParams( + GetLocationPlusOne(caretLocation), + invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, + triggerCharacter: "s", + triggerKind: LSP.CompletionTriggerKind.TriggerForIncompleteCompletions); + + results = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCompletionName, completionParams, CancellationToken.None).ConfigureAwait(false); + actualItem = results.Items.First(i => i.Label == "_someDiscard"); + + Assert.False(results.IsIncomplete); + Assert.NotEmpty(results.ItemDefaults.CommitCharacters); + Assert.Equal("_someDiscard", actualItem.Label); + Assert.Null(actualItem.CommitCharacters); + } + private sealed class CSharpLspThrowExceptionOnChangeCompletionService : CompletionService { private CSharpLspThrowExceptionOnChangeCompletionService(SolutionServices services, IAsynchronousOperationListenerProvider listenerProvider) : base(services, listenerProvider) diff --git a/src/LanguageServer/ProtocolUnitTests/Definitions/GoToTypeDefinitionTests.cs b/src/LanguageServer/ProtocolUnitTests/Definitions/GoToTypeDefinitionTests.cs index a4a1aa043ee23..a18b449d3cd47 100644 --- a/src/LanguageServer/ProtocolUnitTests/Definitions/GoToTypeDefinitionTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Definitions/GoToTypeDefinitionTests.cs @@ -4,10 +4,12 @@ #nullable disable +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using Roslyn.Test.Utilities; +using Roslyn.Test.Utilities.TestGenerators; using Xunit; using Xunit.Abstractions; using LSP = Roslyn.LanguageServer.Protocol; @@ -21,7 +23,7 @@ public GoToTypeDefinitionTests(ITestOutputHelper testOutputHelper) : base(testOu } [Theory, CombinatorialData] - public async Task TestGotoTypeDefinitionAsync(bool mutatingLspWorkspace) + public async Task TestGotoTypeDefinitionAsync_WithTypeSymbol(bool mutatingLspWorkspace) { var markup = @"class {|definition:A|} @@ -37,6 +39,79 @@ class B AssertLocationsEqual(testLspServer.GetLocations("definition"), results); } + [Theory, CombinatorialData] + public async Task TestGotoTypeDefinitionAsync_WithPropertySymbol(bool mutatingLspWorkspace) + { + var markup = +@"class {|definition:A|} +{ +} +class B +{ + A class{|caret:|}A {; +}"; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + + var results = await RunGotoTypeDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); + AssertLocationsEqual(testLspServer.GetLocations("definition"), results); + } + + [Theory, CombinatorialData] + public async Task TestGotoTypeDefinitionAsync_WithFieldSymbol(bool mutatingLspWorkspace) + { + var markup = +@"class {|definition:A|} +{ +} +class B +{ + A class{|caret:|}A; +}"; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + + var results = await RunGotoTypeDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); + AssertLocationsEqual(testLspServer.GetLocations("definition"), results); + } + + [Theory, CombinatorialData] + public async Task TestGotoTypeDefinitionAsync_WithLocalSymbol(bool mutatingLspWorkspace) + { + var markup = +@"class {|definition:A|} +{ +} +class B +{ + void Method() + { + var class{|caret:|}A = new A(); + } +}"; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + + var results = await RunGotoTypeDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); + AssertLocationsEqual(testLspServer.GetLocations("definition"), results); + } + + [Theory, CombinatorialData] + public async Task TestGotoTypeDefinitionAsync_WithParameterSymbol(bool mutatingLspWorkspace) + { + var markup = +@"class {|definition:A|} +{ +} +class B +{ + void Method(A class{|caret:|}A) + { + } +}"; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + + var results = await RunGotoTypeDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); + AssertLocationsEqual(testLspServer.GetLocations("definition"), results); + } + [Theory, CombinatorialData] public async Task TestGotoTypeDefinitionAsync_DifferentDocument(bool mutatingLspWorkspace) { @@ -52,7 +127,7 @@ class {|definition:A|} { class B { - {|caret:|}A classA; + A class{|caret:|}A; } }" }; @@ -81,10 +156,133 @@ class B Assert.Empty(results); } + [Theory, CombinatorialData] + public async Task TestGotoTypeDefinitionAsync_MappedFile(bool mutatingLspWorkspace) + { + var source = + """ + namespace M + { + class A + { + public B b{|caret:|}; + } + } + """; + var mapped = + """ + namespace M + { + class B + { + } + } + """; + + await using var testLspServer = await CreateTestLspServerAsync(source, mutatingLspWorkspace); + + AddMappedDocument(testLspServer.TestWorkspace, mapped); + + var results = await RunGotoTypeDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); + var result = Assert.Single(results); + AssertLocationsEqual([TestSpanMapper.MappedFileLocation], results); + } + + [Theory, CombinatorialData] + public async Task TestGotoTypeDefinitionAsync_SourceGeneratedDocument(bool mutatingLspWorkspace) + { + var source = + """ + namespace M + { + class A + { + public B b{|caret:|}; + } + } + """; + var generated = + """ + namespace M + { + class B + { + } + } + """; + + await using var testLspServer = await CreateTestLspServerAsync(source, mutatingLspWorkspace); + await AddGeneratorAsync(new SingleFileTestGenerator(generated), testLspServer.TestWorkspace); + + var results = await RunGotoTypeDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); + var result = Assert.Single(results); + Assert.Equal(SourceGeneratedDocumentUri.Scheme, result.Uri.Scheme); + } + + [Theory, CombinatorialData] + public async Task TestGotoTypeDefinitionAsync_MetadataAsSource(bool mutatingLspWorkspace) + { + var source = + """ + using System; + class A + { + void Rethrow(NotImplementedException exception) + { + throw {|caret:exception|}; + } + } + """; + + // Create a server with LSP misc file workspace and metadata service. + await using var testLspServer = await CreateTestLspServerAsync(source, mutatingLspWorkspace, new InitializationOptions { ServerKind = WellKnownLspServerKinds.CSharpVisualBasicLspServer }); + + // Get the metadata definition. + var results = await RunGotoTypeDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); + + // Open the metadata file and verify it gets added to the metadata workspace. + await testLspServer.OpenDocumentAsync(results.Single().Uri, text: string.Empty).ConfigureAwait(false); + + Assert.Equal(WorkspaceKind.MetadataAsSource, (await GetWorkspaceForDocument(testLspServer, results.Single().Uri)).Kind); + } + + [Theory, CombinatorialData] + public async Task TestGotoTypeDefinitionAsync_CrossLanguage(bool mutatingLspWorkspace) + { + var markup = +@" + + + public class {|definition:A|} + { + } + + + + Definition + + Class C + Dim {|caret:a|} As A + End Class + + +"; + await using var testLspServer = await CreateXmlTestLspServerAsync(markup, mutatingLspWorkspace); + + var results = await RunGotoTypeDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); + AssertLocationsEqual(testLspServer.GetLocations("definition"), results); + } + private static async Task RunGotoTypeDefinitionAsync(TestLspServer testLspServer, LSP.Location caret) { return await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentTypeDefinitionName, CreateTextDocumentPositionParams(caret), CancellationToken.None); } + + private static async Task GetWorkspaceForDocument(TestLspServer testLspServer, Uri fileUri) + { + var (lspWorkspace, _, _) = await testLspServer.GetManager().GetLspDocumentInfoAsync(new LSP.TextDocumentIdentifier { Uri = fileUri }, CancellationToken.None); + return lspWorkspace!; + } } } diff --git a/src/LanguageServer/ProtocolUnitTests/HandlerTests.cs b/src/LanguageServer/ProtocolUnitTests/HandlerTests.cs index 402b78d7ab003..3787a64d7a2d8 100644 --- a/src/LanguageServer/ProtocolUnitTests/HandlerTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/HandlerTests.cs @@ -10,14 +10,12 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; -using static Microsoft.CodeAnalysis.LanguageServer.UnitTests.LocaleTests; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests { @@ -152,7 +150,7 @@ public async Task NonMutatingHandlerExceptionNFWIsReported(bool mutatingLspWorks var didReport = false; FatalError.OverwriteHandler((exception, severity, dumps) => { - if (exception.Message == nameof(HandlerTests) || exception.InnerException.Message == nameof(HandlerTests)) + if (exception.Message == nameof(HandlerTests) || exception.InnerException?.Message == nameof(HandlerTests)) { didReport = true; } @@ -164,11 +162,6 @@ public async Task NonMutatingHandlerExceptionNFWIsReported(bool mutatingLspWorks await Assert.ThrowsAnyAsync(async () => await server.ExecuteRequestAsync(TestConfigurableDocumentHandler.MethodName, request, CancellationToken.None)); - var provider = server.TestWorkspace.ExportProvider.GetExportedValue(); - await provider.WaitAllDispatcherOperationAndTasksAsync( - server.TestWorkspace, - FeatureAttribute.LanguageServer); - Assert.True(didReport); } @@ -185,7 +178,7 @@ public async Task NonMutatingHandlerExceptionNFWIsNotReportedForLocalRpcExceptio var didReport = false; FatalError.OverwriteHandler((exception, severity, dumps) => { - if (exception.Message == nameof(HandlerTests) || exception.InnerException.Message == nameof(HandlerTests)) + if (exception.Message == nameof(HandlerTests) || exception.InnerException?.Message == nameof(HandlerTests)) { didReport = true; } @@ -197,11 +190,6 @@ public async Task NonMutatingHandlerExceptionNFWIsNotReportedForLocalRpcExceptio await Assert.ThrowsAnyAsync(async () => await server.ExecuteRequestAsync(TestConfigurableDocumentHandler.MethodName, request, CancellationToken.None)); - var provider = server.TestWorkspace.ExportProvider.GetExportedValue(); - await provider.WaitAllDispatcherOperationAndTasksAsync( - server.TestWorkspace, - FeatureAttribute.LanguageServer); - Assert.False(didReport); } @@ -218,7 +206,7 @@ public async Task MutatingHandlerExceptionNFWIsReported(bool mutatingLspWorkspac var didReport = false; FatalError.OverwriteHandler((exception, severity, dumps) => { - if (exception.Message == nameof(HandlerTests) || exception.InnerException.Message == nameof(HandlerTests)) + if (exception.Message == nameof(HandlerTests) || exception.InnerException?.Message == nameof(HandlerTests)) { didReport = true; } @@ -248,7 +236,7 @@ public async Task NonMutatingHandlerCancellationExceptionNFWIsNotReported(bool m var didReport = false; FatalError.OverwriteHandler((exception, severity, dumps) => { - if (exception.Message == nameof(HandlerTests) || exception.InnerException.Message == nameof(HandlerTests)) + if (exception.Message == nameof(HandlerTests) || exception.InnerException?.Message == nameof(HandlerTests)) { didReport = true; } @@ -260,11 +248,6 @@ public async Task NonMutatingHandlerCancellationExceptionNFWIsNotReported(bool m await Assert.ThrowsAnyAsync(async () => await server.ExecuteRequestAsync(TestConfigurableDocumentHandler.MethodName, request, CancellationToken.None)); - var provider = server.TestWorkspace.ExportProvider.GetExportedValue(); - await provider.WaitAllDispatcherOperationAndTasksAsync( - server.TestWorkspace, - FeatureAttribute.LanguageServer); - Assert.False(didReport); } @@ -281,7 +264,7 @@ public async Task MutatingHandlerCancellationExceptionNFWIsNotReported(bool muta var didReport = false; FatalError.OverwriteHandler((exception, severity, dumps) => { - if (exception.Message == nameof(HandlerTests) || exception.InnerException.Message == nameof(HandlerTests)) + if (exception.Message == nameof(HandlerTests) || exception.InnerException?.Message == nameof(HandlerTests)) { didReport = true; } diff --git a/src/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs b/src/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs index 432e223c90903..5968d8f97ca07 100644 --- a/src/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs @@ -105,7 +105,7 @@ void X((int, bool) d) } [Theory, CombinatorialData] - public async Task TestDoesNotShutdownServerIfCacheEntryMissing(bool mutatingLspWorkspace) + public async Task TestReturnsInlayHintsEvenIfCacheMisses(bool mutatingLspWorkspace) { var markup = @"class A @@ -148,12 +148,9 @@ void M() // Assert that the first result id is no longer in the cache. Assert.Null(cache.GetCachedEntry(firstResultId)); - // Assert that the request throws because the item no longer exists in the cache. - await Assert.ThrowsAsync(async () => await testLspServer.ExecuteRequestAsync(LSP.Methods.InlayHintResolveName, firstInlayHint, CancellationToken.None)); - - // Assert that the server did not shutdown and that we can resolve the latest inlay hint request we made. - var lastInlayHint = await testLspServer.ExecuteRequestAsync(LSP.Methods.InlayHintResolveName, lastInlayHints.First(), CancellationToken.None); - Assert.NotNull(lastInlayHint?.ToolTip); + // Assert that the resolve request returns the inlay hint even if not in the cache. + var firstResolvedHint = await testLspServer.ExecuteRequestAsync(LSP.Methods.InlayHintResolveName, firstInlayHint, CancellationToken.None); + Assert.NotNull(firstResolvedHint?.ToolTip); } private async Task RunVerifyInlayHintAsync(string markup, bool mutatingLspWorkspace, bool hasTextEdits = true) diff --git a/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs b/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs index f9422d9432c00..47b09605f0dc1 100644 --- a/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs @@ -203,7 +203,7 @@ void M() await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); - Assert.NotNull(results[0].Location.Uri); + Assert.NotNull(results[0].Location!.Uri); AssertHighlightCount(results, expectedDefinitionCount: 0, expectedWrittenReferenceCount: 0, expectedReferenceCount: 1); } @@ -270,6 +270,37 @@ public async Task TestFindAllReferencesAsync_StaticClassification(bool mutatingL Assert.Equal(9, textRuns.Count()); } + [Theory, CombinatorialData] + public async Task TestFindAllReferencesAsync_PreprocessingSymbol(bool mutatingLspWorkspace) + { + var markup = + """ + #define {|reference:PREPROCESSING_SYMBOL|} + #define MORE_PREPROCESSING_SYMBOL + + #if {|reference:PREPROCESSING_SYMBOL|} + namespace SimpleNamespace; + #elif true && (!false || {|caret:|}{|reference:PREPROCESSING_SYMBOL|}) + namespace AnotherNamespace; + #elif MORE_PREPROCESSING_SYMBOL + namespace MoreSimpleNamespace; + #else + namespace ComplexNamespace; + #endif + + // PREPROCESSING_SYMBOL + class PREPROCESSING_SYMBOL + { + } + """; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, CapabilitiesWithVSExtensions); + + var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); + + // Do not assert the glyph + AssertHighlightCount(results, expectedDefinitionCount: 0, expectedWrittenReferenceCount: 0, expectedReferenceCount: 3); + } + private static LSP.ReferenceParams CreateReferenceParams(LSP.Location caret, IProgress progress) => new LSP.ReferenceParams() { diff --git a/src/LanguageServer/ProtocolUnitTests/RelatedDocuments/RelatedDocumentsTests.cs b/src/LanguageServer/ProtocolUnitTests/RelatedDocuments/RelatedDocumentsTests.cs index 2cc292339e553..6679d9089c729 100644 --- a/src/LanguageServer/ProtocolUnitTests/RelatedDocuments/RelatedDocumentsTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/RelatedDocuments/RelatedDocumentsTests.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; +using Roslyn.Test.Utilities.TestGenerators; using Roslyn.Utilities; using Xunit; using Xunit.Abstractions; @@ -174,4 +175,39 @@ class Y AssertJsonEquals(results2, expectedResult); } + + [Theory, CombinatorialData] + public async Task DoesNotIncludeSourceGeneratedDocuments(bool mutatingLspWorkspace, bool useProgress) + { + var source = + """ + namespace M + { + class A + { + public {|caret:|}B b; + } + } + """; + var generated = + """ + namespace M + { + class B + { + } + } + """; + + await using var testLspServer = await CreateTestLspServerAsync(source, mutatingLspWorkspace); + await AddGeneratorAsync(new SingleFileTestGenerator(generated), testLspServer.TestWorkspace); + + var project = testLspServer.TestWorkspace.CurrentSolution.Projects.Single(); + var results = await RunGetRelatedDocumentsAsync( + testLspServer, + project.Documents.First().GetURI(), + useProgress: useProgress); + + Assert.Empty(results); + } } diff --git a/src/LanguageServer/ProtocolUnitTests/Workspaces/SourceGeneratedDocumentTests.cs b/src/LanguageServer/ProtocolUnitTests/Workspaces/SourceGeneratedDocumentTests.cs index 1889e398f460e..1d6ae9f07598d 100644 --- a/src/LanguageServer/ProtocolUnitTests/Workspaces/SourceGeneratedDocumentTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Workspaces/SourceGeneratedDocumentTests.cs @@ -2,13 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CodeAnalysis.LanguageServer.Handler.SourceGenerators; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Roslyn.Test.Utilities.TestGenerators; +using Roslyn.Utilities; using Xunit; using Xunit.Abstractions; using LSP = Roslyn.LanguageServer.Protocol; @@ -27,7 +32,7 @@ public async Task ReturnsTextForSourceGeneratedDocument(bool mutatingLspWorkspac var sourceGeneratorDocumentUri = SourceGeneratedDocumentUri.Create(sourceGeneratedDocumentIdentity); var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, - new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }), CancellationToken.None); + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); AssertEx.NotNull(text); Assert.Equal("// Hello, World", text.Text); @@ -43,7 +48,7 @@ public async Task OpenCloseSourceGeneratedDocument(bool mutatingLspWorkspace) var sourceGeneratorDocumentUri = SourceGeneratedDocumentUri.Create(sourceGeneratedDocumentIdentity); var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, - new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }), CancellationToken.None); + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); AssertEx.NotNull(text); Assert.Equal("// Hello, World", text.Text); @@ -68,7 +73,7 @@ public async Task OpenMultipleSourceGeneratedDocument(bool mutatingLspWorkspace) foreach (var sourceGeneratorDocumentUri in sourceGeneratorDocumentUris) { var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, - new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }), CancellationToken.None); + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); AssertEx.NotNull(text?.Text); await testLspServer.OpenDocumentAsync(sourceGeneratorDocumentUri, text.Text); } @@ -97,6 +102,191 @@ public async Task RequestOnSourceGeneratedDocument(bool mutatingLspWorkspace) Assert.Contains("class A", hover.Contents.Fourth.Value); } + [Theory, CombinatorialData] + public async Task ReturnsGeneratedSourceForOpenDocument(bool mutatingLspWorkspace) + { + var sourceGeneratorSource = "// Hello, World"; + await using var testLspServer = await CreateTestLspServerWithGeneratorAsync(mutatingLspWorkspace, sourceGeneratorSource); + + var sourceGeneratedDocuments = await testLspServer.GetCurrentSolution().Projects.Single().GetSourceGeneratedDocumentsAsync(); + var sourceGeneratedDocumentIdentity = sourceGeneratedDocuments.Single().Identity; + var sourceGeneratorDocumentUri = SourceGeneratedDocumentUri.Create(sourceGeneratedDocumentIdentity); + + // Open the document with different text - this will cause the queue to generate frozen sg documents using this value. + // However the get text handler should return the real source generator source. + await testLspServer.OpenDocumentAsync(sourceGeneratorDocumentUri, "LSP Open Document Text"); + + var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); + + AssertEx.NotNull(text); + Assert.Equal(sourceGeneratorSource, text.Text); + } + + [Theory, CombinatorialData] + public async Task TestReturnsUnchangedResult(bool mutatingLspWorkspace) + { + await using var testLspServer = await CreateTestLspServerWithGeneratorAsync(mutatingLspWorkspace, "// Hello, World"); + + var sourceGeneratedDocuments = await testLspServer.GetCurrentSolution().Projects.Single().GetSourceGeneratedDocumentsAsync(); + var sourceGeneratedDocumentIdentity = sourceGeneratedDocuments.Single().Identity; + var sourceGeneratorDocumentUri = SourceGeneratedDocumentUri.Create(sourceGeneratedDocumentIdentity); + + var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); + + AssertEx.NotNull(text); + Assert.Equal("// Hello, World", text.Text); + + // Make a second request - since nothing has changed we should get back the same resultId. + var secondRequest = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: text.ResultId), CancellationToken.None); + AssertEx.NotNull(secondRequest); + Assert.Null(secondRequest.Text); + Assert.Equal(text.ResultId, secondRequest.ResultId); + } + + [Theory, CombinatorialData] + internal async Task TestReturnsGeneratedSourceWhenDocumentChanges(bool mutatingLspWorkspace, SourceGeneratorExecutionPreference sourceGeneratorExecution) + { + await using var testLspServer = await CreateTestLspServerAsync(string.Empty, mutatingLspWorkspace); + + var configService = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); + configService.Options = new WorkspaceConfigurationOptions(SourceGeneratorExecution: sourceGeneratorExecution); + + var callCount = 0; + var generatorReference = await AddGeneratorAsync(new CallbackGenerator(() => ("hintName.cs", "// callCount: " + callCount++)), testLspServer.TestWorkspace); + + var sourceGeneratedDocuments = await testLspServer.GetCurrentSolution().Projects.Single().GetSourceGeneratedDocumentsAsync(); + var sourceGeneratedDocumentIdentity = sourceGeneratedDocuments.Single().Identity; + var sourceGeneratorDocumentUri = SourceGeneratedDocumentUri.Create(sourceGeneratedDocumentIdentity); + + var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); + + AssertEx.NotNull(text); + Assert.Equal("// callCount: 0", text.Text); + + // Modify a normal document in the workspace. + // In automatic mode this should trigger generators to re-run. + // In balanced mode generators should not re-run. + await testLspServer.TestWorkspace.ChangeDocumentAsync(testLspServer.TestWorkspace.Documents.Single(d => !d.IsSourceGenerated).Id, SourceText.From("new text")); + await WaitForSourceGeneratorsAsync(testLspServer.TestWorkspace); + + // Ask for the source generated text again. + var secondRequest = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: text.ResultId), CancellationToken.None); + + if (sourceGeneratorExecution == SourceGeneratorExecutionPreference.Automatic) + { + // We should get newly generated text + AssertEx.NotNull(secondRequest); + Assert.NotEqual(text.ResultId, secondRequest.ResultId); + Assert.Equal("// callCount: 1", secondRequest.Text); + } + else + { + // We should get an unchanged result + AssertEx.NotNull(secondRequest); + Assert.Equal(text.ResultId, secondRequest.ResultId); + Assert.Null(secondRequest.Text); + } + } + + [Theory, CombinatorialData] + internal async Task TestReturnsGeneratedSourceWhenManuallyRefreshed(bool mutatingLspWorkspace, SourceGeneratorExecutionPreference sourceGeneratorExecution) + { + await using var testLspServer = await CreateTestLspServerAsync(string.Empty, mutatingLspWorkspace); + + var configService = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); + configService.Options = new WorkspaceConfigurationOptions(SourceGeneratorExecution: sourceGeneratorExecution); + + var callCount = 0; + var generatorReference = await AddGeneratorAsync(new CallbackGenerator(() => ("hintName.cs", "// callCount: " + callCount++)), testLspServer.TestWorkspace); + + var sourceGeneratedDocuments = await testLspServer.GetCurrentSolution().Projects.Single().GetSourceGeneratedDocumentsAsync(); + var sourceGeneratedDocumentIdentity = sourceGeneratedDocuments.Single().Identity; + var sourceGeneratorDocumentUri = SourceGeneratedDocumentUri.Create(sourceGeneratedDocumentIdentity); + + var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); + + AssertEx.NotNull(text); + Assert.Equal("// callCount: 0", text.Text); + + // Updating the execution version should trigger source generators to run in both automatic and balanced mode. + testLspServer.TestWorkspace.EnqueueUpdateSourceGeneratorVersion(projectId: null, forceRegeneration: true); + await WaitForSourceGeneratorsAsync(testLspServer.TestWorkspace); + + var secondRequest = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: text.ResultId), CancellationToken.None); + AssertEx.NotNull(secondRequest); + Assert.NotEqual(text.ResultId, secondRequest.ResultId); + Assert.Equal("// callCount: 1", secondRequest.Text); + } + + [Theory, CombinatorialData] + public async Task TestReturnsNullForRemovedClosedGeneratedFile(bool mutatingLspWorkspace) + { + var generatorText = "// Hello, World"; + await using var testLspServer = await CreateTestLspServerAsync(string.Empty, mutatingLspWorkspace); + var generatorReference = await AddGeneratorAsync(new SingleFileTestGenerator(generatorText), testLspServer.TestWorkspace); + + var sourceGeneratedDocuments = await testLspServer.GetCurrentSolution().Projects.Single().GetSourceGeneratedDocumentsAsync(); + var sourceGeneratedDocumentIdentity = sourceGeneratedDocuments.Single().Identity; + var sourceGeneratorDocumentUri = SourceGeneratedDocumentUri.Create(sourceGeneratedDocumentIdentity); + + var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); + AssertEx.NotNull(text); + Assert.Equal("// Hello, World", text.Text); + + // Remove the generator and verify that we get null text back. + await RemoveGeneratorAsync(generatorReference, testLspServer.TestWorkspace); + + var secondRequest = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: text.ResultId), CancellationToken.None); + + Assert.NotNull(secondRequest); + Assert.Null(secondRequest.Text); + } + + [Theory, CombinatorialData] + public async Task TestReturnsNullForRemovedOpenedGeneratedFile(bool mutatingLspWorkspace) + { + var generatorText = "// Hello, World"; + await using var testLspServer = await CreateTestLspServerAsync(string.Empty, mutatingLspWorkspace); + var generatorReference = await AddGeneratorAsync(new SingleFileTestGenerator(generatorText), testLspServer.TestWorkspace); + + var sourceGeneratedDocuments = await testLspServer.GetCurrentSolution().Projects.Single().GetSourceGeneratedDocumentsAsync(); + var sourceGeneratedDocumentIdentity = sourceGeneratedDocuments.Single().Identity; + var sourceGeneratorDocumentUri = SourceGeneratedDocumentUri.Create(sourceGeneratedDocumentIdentity); + + var text = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: null), CancellationToken.None); + AssertEx.NotNull(text); + Assert.Equal("// Hello, World", text.Text); + + // Open the document - this will cause the queue to generate frozen sg documents based on the LSP open text + // even if the source generator is removed entirely. + await testLspServer.OpenDocumentAsync(sourceGeneratorDocumentUri, text.Text); + + // Remove the generator - the handler should return null text. + await RemoveGeneratorAsync(generatorReference, testLspServer.TestWorkspace); + + var secondRequest = await testLspServer.ExecuteRequestAsync(SourceGeneratedDocumentGetTextHandler.MethodName, + new SourceGeneratorGetTextParams(new LSP.TextDocumentIdentifier { Uri = sourceGeneratorDocumentUri }, ResultId: text.ResultId), CancellationToken.None); + + Assert.NotNull(secondRequest); + Assert.Null(secondRequest.Text); + } + + private static async Task WaitForSourceGeneratorsAsync(EditorTestWorkspace workspace) + { + var operations = workspace.ExportProvider.GetExportedValue(); + await operations.WaitAllAsync(workspace, [FeatureAttribute.Workspace, FeatureAttribute.SourceGenerators]); + } + private async Task CreateTestLspServerWithGeneratorAsync(bool mutatingLspWorkspace, string generatedDocumentText) { var testLspServer = await CreateTestLspServerAsync(string.Empty, mutatingLspWorkspace); diff --git a/src/Tools/BuildActionTelemetryTable/Program.cs b/src/Tools/BuildActionTelemetryTable/Program.cs index d03b7331e2d4e..e0878467c011b 100644 --- a/src/Tools/BuildActionTelemetryTable/Program.cs +++ b/src/Tools/BuildActionTelemetryTable/Program.cs @@ -402,7 +402,7 @@ public class Program { "Microsoft.CodeAnalysis.VisualBasic.ReplaceConditionalWithStatements.VisualBasicReplaceConditionalWithStatementsCodeRefactoringProvider", "Replace Conditional With Statements (Refactoring)" }, { "Microsoft.CodeAnalysis.VisualBasic.ReplaceDocCommentTextWithTag.VisualBasicReplaceDocCommentTextWithTagCodeRefactoringProvider", "Replace Doc Comment Text With Tag (Refactoring)" }, { "Microsoft.CodeAnalysis.VisualBasic.SimplifyInterpolation.VisualBasicSimplifyInterpolationCodeFixProvider", "Simplify Interpolation" }, - { "Microsoft.CodeAnalysis.VisualBasic.SimplifyLinqExpression.VisualBasicSimplifyLinqExpressionCodeFixProvider", "Simplify Linq Expression" }, + { "Microsoft.CodeAnalysis.VisualBasic.SimplifyLinqExpression.SimplifyLinqExpressionCodeFixProvider", "Simplify Linq Expression" }, { "Microsoft.CodeAnalysis.VisualBasic.SimplifyObjectCreation.VisualBasicSimplifyObjectCreationCodeFixProvider", "Simplify Object Creation" }, { "Microsoft.CodeAnalysis.VisualBasic.SimplifyThisOrMe.VisualBasicSimplifyThisOrMeCodeFixProvider", "Simplify This Or Me" }, { "Microsoft.CodeAnalysis.VisualBasic.SimplifyTypeNames.SimplifyTypeNamesCodeFixProvider", "Simplify Type Names" }, diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/CodeActions.cs b/src/Tools/ExternalAccess/Razor/Cohost/Handlers/CodeActions.cs new file mode 100644 index 0000000000000..1fe5e62b48853 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Cohost/Handlers/CodeActions.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Razor; +using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; +using Roslyn.LanguageServer.Protocol; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost.Handlers; + +internal static class CodeActions +{ + public static Task GetCodeActionsAsync( + Document document, + CodeActionParams request, + bool supportsVSExtensions, + CancellationToken cancellationToken) + { + var solution = document.Project.Solution; + + var codeFixService = solution.Services.ExportProvider.GetService(); + var codeRefactoringService = solution.Services.ExportProvider.GetService(); + + return CodeActionHelpers.GetVSCodeActionsAsync(request, document, codeFixService, codeRefactoringService, supportsVSExtensions, cancellationToken); + } + + public static async Task ResolveCodeActionAsync(Document document, CodeAction codeAction, ResourceOperationKind[] resourceOperations, CancellationToken cancellationToken) + { + Contract.ThrowIfNull(codeAction.Data); + var data = CodeActionResolveHandler.GetCodeActionResolveData(codeAction); + Assumes.Present(data); + + // We don't need to resolve a top level code action that has nested actions - it requires further action + // on the client to pick which of the nested actions to actually apply. + if (data.NestedCodeActions.HasValue && data.NestedCodeActions.Value.Length > 0) + { + return codeAction; + } + + var solution = document.Project.Solution; + + var codeFixService = solution.Services.ExportProvider.GetService(); + var codeRefactoringService = solution.Services.ExportProvider.GetService(); + + var codeActions = await CodeActionHelpers.GetCodeActionsAsync( + document, + data.Range, + codeFixService, + codeRefactoringService, + fixAllScope: null, + cancellationToken).ConfigureAwait(false); + + Contract.ThrowIfNull(data.CodeActionPath); + var codeActionToResolve = CodeActionHelpers.GetCodeActionToResolve(data.CodeActionPath, codeActions, isFixAllAction: false); + + var operations = await codeActionToResolve.GetOperationsAsync(solution, CodeAnalysisProgress.None, cancellationToken).ConfigureAwait(false); + + var edit = await CodeActionResolveHelper.GetCodeActionResolveEditsAsync( + solution, + data, + operations, + resourceOperations, + logFunction: static s => { }, + cancellationToken).ConfigureAwait(false); + + codeAction.Edit = edit; + return codeAction; + } + + public static Task GetFormattedNewFileContentAsync(Document document, CancellationToken cancellationToken) + => FormatNewFileHandler.GetFormattedNewFileContentAsync(document, cancellationToken); + + public static Task GetSimplifiedEditsAsync(Document document, TextEdit textEdit, CancellationToken cancellationToken) + => SimplifyMethodHandler.GetSimplifiedEditsAsync(document, textEdit, cancellationToken); +} diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs b/src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs index 1c7fe47a2ea68..c04cc390f5090 100644 --- a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs +++ b/src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs @@ -26,6 +26,21 @@ internal static class InlayHints // always just result in the defaults, which for inline hints are to not show anything. However, the editor has a // setting for LSP inlay hints, so we can assume that if we get a request from the client, the user wants hints. // When overriding however, Roslyn does a nicer job if type hints are off. + var options = GetOptions(displayAllOverride); + + return InlayHintHandler.GetInlayHintsAsync(document, textDocumentIdentifier, range, options, displayAllOverride, s_resolveCache, cancellationToken); + } + + public static Task ResolveInlayHintAsync(Document document, InlayHint request, CancellationToken cancellationToken) + { + Contract.ThrowIfNull(s_resolveCache, "Cache should never be null for resolve, since it should have been created by the original request"); + var data = InlayHintResolveHandler.GetInlayHintResolveData(request); + var options = GetOptions(data.DisplayAllOverride); + return InlayHintResolveHandler.ResolveInlayHintAsync(document, request, s_resolveCache, data, options, cancellationToken); + } + + private static InlineHintsOptions GetOptions(bool displayAllOverride) + { var options = InlineHintsOptions.Default; if (!displayAllOverride) { @@ -36,14 +51,7 @@ internal static class InlayHints }; } - return InlayHintHandler.GetInlayHintsAsync(document, textDocumentIdentifier, range, options, displayAllOverride, s_resolveCache, cancellationToken); - } - - public static Task ResolveInlayHintAsync(Document document, InlayHint request, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(s_resolveCache, "Cache should never be null for resolve, since it should have been created by the original request"); - - return InlayHintResolveHandler.ResolveInlayHintAsync(document, request, s_resolveCache, cancellationToken); + return options; } } } diff --git a/src/Tools/IdeCoreBenchmarks/SegmentedDictionaryBenchmarks_Add.cs b/src/Tools/IdeCoreBenchmarks/SegmentedDictionaryBenchmarks_Add.cs new file mode 100644 index 0000000000000..15b1b9984127b --- /dev/null +++ b/src/Tools/IdeCoreBenchmarks/SegmentedDictionaryBenchmarks_Add.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using BenchmarkDotNet.Attributes; +using Microsoft.CodeAnalysis.Collections; + +namespace IdeCoreBenchmarks +{ + [MemoryDiagnoser] + public class SegmentedDictionaryBenchmarks_Add + { + [Params(1_000, 10_000, 100_000, 1_000_000)] + public int Count { get; set; } + + private int[]? _intItems; + private object[]? _objectItems; + private LargeStruct[]? _largeItems; + private EnormousStruct[]? _enormousItems; + + [IterationSetup] + public void IterationSetup() + { + _intItems = new int[Count]; + _objectItems = new object[Count]; + _largeItems = new LargeStruct[Count]; + _enormousItems = new EnormousStruct[Count]; + + for (var i = 0; i < Count; i++) + { + _intItems[i] = i; + _objectItems[i] = new object(); + _largeItems[i] = new LargeStruct() { s1 = new MediumStruct() { i1 = i } }; + _enormousItems[i] = new EnormousStruct() { s1 = _largeItems[i] }; + } + } + + [Benchmark] + public void AddIntToList() + => AddToList(_intItems!); + + [Benchmark] + public void AddObjectToList() + => AddToList(_objectItems!); + + [Benchmark] + public void AddLargeStructToList() + => AddToList(_largeItems!); + + [Benchmark] + public void AddEnormousStructToList() + => AddToList(_enormousItems!); + + private void AddToList(T[] items) where T : notnull + { + var dict = new SegmentedDictionary(); + var iterations = Count; + + for (var i = 0; i < iterations; i++) + dict.Add(items[i], items[i]); + } + + private struct MediumStruct + { + public int i1 { get; set; } + public int i2 { get; set; } + public int i3 { get; set; } + public int i4 { get; set; } + public int i5 { get; set; } + } + + private struct LargeStruct + { + public MediumStruct s1 { get; set; } + public MediumStruct s2 { get; set; } + public MediumStruct s3 { get; set; } + public MediumStruct s4 { get; set; } + } + + private struct EnormousStruct + { + public LargeStruct s1 { get; set; } + public LargeStruct s2 { get; set; } + public LargeStruct s3 { get; set; } + public LargeStruct s4 { get; set; } + public LargeStruct s5 { get; set; } + public LargeStruct s6 { get; set; } + public LargeStruct s7 { get; set; } + public LargeStruct s8 { get; set; } + public LargeStruct s9 { get; set; } + public LargeStruct s10 { get; set; } + } + } +} diff --git a/src/Tools/IdeCoreBenchmarks/SegmentedListBenchmarks_Add.cs b/src/Tools/IdeCoreBenchmarks/SegmentedListBenchmarks_Add.cs new file mode 100644 index 0000000000000..5c5d77bbfd221 --- /dev/null +++ b/src/Tools/IdeCoreBenchmarks/SegmentedListBenchmarks_Add.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using BenchmarkDotNet.Attributes; +using Microsoft.CodeAnalysis.Collections; + +namespace IdeCoreBenchmarks +{ + [MemoryDiagnoser] + public class SegmentedListBenchmarks_Add + { + [Params(1_000, 10_000, 100_000, 1_000_000)] + public int Count { get; set; } + + [Benchmark] + public void AddIntToList() + => AddToList(1); + + [Benchmark] + public void AddObjectToList() + => AddToList(new object()); + + [Benchmark] + public void AddLargeStructToList() + => AddToList(new LargeStruct()); + + [Benchmark] + public void AddEnormousStructToList() + => AddToList(new EnormousStruct()); + + private void AddToList(T item) + { + var array = new SegmentedList(); + var iterations = Count; + + for (var i = 0; i < iterations; i++) + array.Add(item); + } + + private struct MediumStruct + { + public int i1 { get; set; } + public int i2 { get; set; } + public int i3 { get; set; } + public int i4 { get; set; } + public int i5 { get; set; } + } + + private struct LargeStruct + { + public MediumStruct s1 { get; set; } + public MediumStruct s2 { get; set; } + public MediumStruct s3 { get; set; } + public MediumStruct s4 { get; set; } + } + + private struct EnormousStruct + { + public LargeStruct s1 { get; set; } + public LargeStruct s2 { get; set; } + public LargeStruct s3 { get; set; } + public LargeStruct s4 { get; set; } + public LargeStruct s5 { get; set; } + public LargeStruct s6 { get; set; } + public LargeStruct s7 { get; set; } + public LargeStruct s8 { get; set; } + public LargeStruct s9 { get; set; } + public LargeStruct s10 { get; set; } + } + } +} diff --git a/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs b/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs index f0e072481b59b..c2bcace02d039 100644 --- a/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs +++ b/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs @@ -114,6 +114,7 @@ private static IEnumerable GetExpressionCodeStyleOptions(Tiere yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferRangeOperator, ServicesVSResources.Prefer_range_operator, options, updater); yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.ImplicitObjectCreationWhenTypeIsApparent, CSharpVSResources.Prefer_implicit_object_creation_when_type_is_apparent, options, updater); yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferTupleSwap, ServicesVSResources.Prefer_tuple_swap, options, updater); + yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferUnboundGenericTypeInNameOf, ServicesVSResources.Prefer_unbound_generic_type_in_nameof, options, updater); yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferUtf8StringLiterals, ServicesVSResources.Prefer_Utf8_string_literals, options, updater); } diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixer.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixer.cs index 95869ad79ed84..b9d7c7e036479 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixer.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixer.cs @@ -7,23 +7,19 @@ using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; using Microsoft.VisualStudio.LanguageServices.Implementation.CodeCleanup; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Utilities; -namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService -{ - [Export(typeof(AbstractCodeCleanUpFixer))] - [ContentType(ContentTypeNames.CSharpContentType)] - internal sealed class CSharpCodeCleanUpFixer : AbstractCodeCleanUpFixer - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpCodeCleanUpFixer(IThreadingContext threadingContext, VisualStudioWorkspaceImpl workspace, IVsHierarchyItemManager vsHierarchyItemManager) - : base(threadingContext, workspace, vsHierarchyItemManager) - { - } - } -} +namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService; + +[Export(typeof(AbstractCodeCleanUpFixer))] +[ContentType(ContentTypeNames.CSharpContentType)] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpCodeCleanUpFixer( + IThreadingContext threadingContext, + VisualStudioWorkspaceImpl workspace, + IVsHierarchyItemManager vsHierarchyItemManager) + : AbstractCodeCleanUpFixer(threadingContext, workspace, vsHierarchyItemManager); diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs index 72630c261a81d..20e4dd353019b 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs @@ -208,6 +208,11 @@ private bool TryGetTextForSymbol( return true; } + if (symbol is IPreprocessingSymbol) + { + Debug.Fail("We should have handled that in the preprocessor directive."); + } + text = FormatSymbol(symbol); return text != null; } @@ -357,7 +362,7 @@ private static bool TryGetTextForPreProcessor(SyntaxToken token, [NotNullWhen(tr return true; } - if (token.IsKind(SyntaxKind.EndOfDirectiveToken)) + if (token.Kind() is SyntaxKind.IdentifierToken or SyntaxKind.EndOfDirectiveToken) { text = $"#{directive.HashToken.GetNextToken(includeDirectives: true).Text}"; return true; diff --git a/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpEntryPointFinder.cs b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpEntryPointFinder.cs new file mode 100644 index 0000000000000..3e2326480ee17 --- /dev/null +++ b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpEntryPointFinder.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; + +namespace Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim; + +internal sealed class CSharpEntryPointFinder(Compilation compilation) + : AbstractEntryPointFinder(compilation) +{ + protected override bool MatchesMainMethodName(string name) + => name == "Main"; + + public static IEnumerable FindEntryPoints(Compilation compilation) + { + // This differs from the VB implementation + // (Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim.EntryPointFinder) because we don't + // ever consider forms entry points. Technically, this is wrong but it just doesn't matter since the ref + // assemblies are unlikely to have a random Main() method that matches + var visitor = new CSharpEntryPointFinder(compilation); + visitor.Visit(compilation.SourceModule.GlobalNamespace); + return visitor.EntryPoints; + } +} diff --git a/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpEntryPointFinderService.cs b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpEntryPointFinderService.cs index c55a36e221c83..9c20550382e30 100644 --- a/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpEntryPointFinderService.cs +++ b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpEntryPointFinderService.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Generic; using System.Composition; @@ -11,18 +9,13 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; -namespace Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim -{ - [ExportLanguageService(typeof(IEntryPointFinderService), LanguageNames.CSharp), Shared] - internal class CSharpEntryPointFinderService : IEntryPointFinderService - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpEntryPointFinderService() - { - } +namespace Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim; - public IEnumerable FindEntryPoints(INamespaceSymbol symbol, bool findFormsOnly) - => EntryPointFinder.FindEntryPoints(symbol); - } +[ExportLanguageService(typeof(IEntryPointFinderService), LanguageNames.CSharp), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpEntryPointFinderService() : AbstractEntryPointFinderService +{ + protected override IEnumerable FindEntryPoints(Compilation compilation, bool findFormsOnly) + => CSharpEntryPointFinder.FindEntryPoints(compilation); } diff --git a/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpProjectShim.ICSharpProjectSite.cs b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpProjectShim.ICSharpProjectSite.cs index b87a4b0820fb1..7992e546c7411 100644 --- a/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpProjectShim.ICSharpProjectSite.cs +++ b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpProjectShim.ICSharpProjectSite.cs @@ -122,7 +122,7 @@ public int GetValidStartupClasses(IntPtr[] classNames, ref int count) { var project = Workspace.CurrentSolution.GetRequiredProject(ProjectSystemProject.Id); var compilation = project.GetRequiredCompilationAsync(CancellationToken.None).WaitAndGetResult(CancellationToken.None); - var entryPoints = EntryPointFinder.FindEntryPoints(compilation.SourceModule.GlobalNamespace); + var entryPoints = CSharpEntryPointFinder.FindEntryPoints(compilation); // If classNames is NULL, then we need to populate the number of valid startup // classes only diff --git a/src/VisualStudio/CSharp/Impl/ProjectSystemShim/EntryPointFinder.cs b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/EntryPointFinder.cs deleted file mode 100644 index ec37ee38cbafd..0000000000000 --- a/src/VisualStudio/CSharp/Impl/ProjectSystemShim/EntryPointFinder.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using Microsoft.CodeAnalysis; -using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; - -namespace Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim -{ - internal class EntryPointFinder : AbstractEntryPointFinder - { - protected override bool MatchesMainMethodName(string name) - => name == "Main"; - - public static IEnumerable FindEntryPoints(INamespaceSymbol symbol) - { - // This differs from the VB implementation (Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim.EntryPointFinder) - // because we don't ever consider forms entry points. - // Techinically, this is wrong but it just doesn't matter since the - // ref assemblies are unlikely to have a random Main() method that matches - var visitor = new EntryPointFinder(); - visitor.Visit(symbol); - return visitor.EntryPoints; - } - } -} diff --git a/src/VisualStudio/CSharp/Test/EditorConfigSettings/DataProvider/DataProviderTests.cs b/src/VisualStudio/CSharp/Test/EditorConfigSettings/DataProvider/DataProviderTests.cs index cb15c66efafbc..1b829db2339a3 100644 --- a/src/VisualStudio/CSharp/Test/EditorConfigSettings/DataProvider/DataProviderTests.cs +++ b/src/VisualStudio/CSharp/Test/EditorConfigSettings/DataProvider/DataProviderTests.cs @@ -171,7 +171,7 @@ public void TestGettingCodeStyleSettingsProviderLanguageServiceAsync() var optionsWithUI = CSharpCodeStyleOptions.EditorConfigOptions .Remove(CSharpCodeStyleOptions.PreferredModifierOrder); - AssertEx.SetEqual(optionsWithUI, dataSnapShot.Select(setting => setting.Key.Option)); + AssertEx.SetEqual(optionsWithUI.OrderBy(o => o.Name), dataSnapShot.Select(setting => setting.Key.Option).OrderBy(o => o.Name)); } [Fact] diff --git a/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs b/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs index 0aaf3925e510f..fc030c088c745 100644 --- a/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs +++ b/src/VisualStudio/CSharp/Test/F1Help/F1HelpTests.cs @@ -2,8 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -16,2025 +15,2289 @@ using Roslyn.Utilities; using Xunit; -namespace Microsoft.VisualStudio.LanguageServices.CSharp.UnitTests.F1Help -{ - [UseExportProvider] - [Trait(Traits.Feature, Traits.Features.F1Help)] - public class F1HelpTests - { - private static async Task TestAsync(string markup, string expectedText) - { - using var workspace = TestWorkspace.CreateCSharp(markup, composition: VisualStudioTestCompositions.LanguageServices); - var caret = workspace.Documents.First().CursorPosition; - - var service = Assert.IsType(workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetService()); - var actualText = await service.GetHelpTermAsync(workspace.CurrentSolution.Projects.First().Documents.First(), workspace.Documents.First().SelectedSpans.First(), CancellationToken.None); - Assert.Equal(expectedText, actualText); - } - - private static async Task Test_KeywordAsync(string markup, string expectedText) - { - await TestAsync(markup, expectedText + "_CSharpKeyword"); - } - - [Fact] - public async Task TestInternal() - { - await Test_KeywordAsync( -@"intern[||]al class C -{ -}", "internal"); - } - - [Fact] - public async Task TestProtected() - { - await Test_KeywordAsync( -@"public class C -{ - protec[||]ted void goo(); -}", "protected"); - } - - [Fact] - public async Task TestProtectedInternal1() - { - await Test_KeywordAsync( -@"public class C -{ - internal protec[||]ted void goo(); -}", "protectedinternal"); - } - - [Fact] - public async Task TestProtectedInternal2() - { - await Test_KeywordAsync( -@"public class C -{ - protec[||]ted internal void goo(); -}", "protectedinternal"); - } - - [Fact] - public async Task TestPrivateProtected1() - { - await Test_KeywordAsync( -@"public class C -{ - private protec[||]ted void goo(); -}", "privateprotected"); - } - - [Fact] - public async Task TestPrivateProtected2() - { - await Test_KeywordAsync( -@"public class C -{ - priv[||]ate protected void goo(); -}", "privateprotected"); - } - - [Fact] - public async Task TestPrivateProtected3() - { - await Test_KeywordAsync( -@"public class C -{ - protected priv[||]ate void goo(); -}", "privateprotected"); - } - - [Fact] - public async Task TestPrivateProtected4() - { - await Test_KeywordAsync( -@"public class C +namespace Microsoft.VisualStudio.LanguageServices.CSharp.UnitTests.F1Help; + +[UseExportProvider] +[Trait(Traits.Feature, Traits.Features.F1Help)] +public sealed class F1HelpTests { - prot[||]ected private void goo(); -}", "privateprotected"); - } - - [Fact] - public async Task TestModifierSoup() - { - await Test_KeywordAsync( - @"public class C + private static async Task TestAsync([StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, string expectedText) + { + using var workspace = TestWorkspace.CreateCSharp(markup, composition: VisualStudioTestCompositions.LanguageServices); + var caret = workspace.Documents.First().CursorPosition; + + var service = Assert.IsType(workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetService()); + var actualText = await service.GetHelpTermAsync(workspace.CurrentSolution.Projects.First().Documents.First(), workspace.Documents.First().SelectedSpans.First(), CancellationToken.None); + Assert.Equal(expectedText, actualText); + } + + private static async Task Test_KeywordAsync([StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, string expectedText) + { + await TestAsync(markup, expectedText + "_CSharpKeyword"); + } + + [Fact] + public async Task TestInternal() + { + await Test_KeywordAsync( + """ + intern[||]al class C + { + } + """, "internal"); + } + + [Fact] + public async Task TestProtected() + { + await Test_KeywordAsync( + """ + public class C + { + protec[||]ted void goo(); + } + """, "protected"); + } + + [Fact] + public async Task TestProtectedInternal1() + { + await Test_KeywordAsync( + """ + public class C + { + internal protec[||]ted void goo(); + } + """, "protectedinternal"); + } + + [Fact] + public async Task TestProtectedInternal2() + { + await Test_KeywordAsync( + """ + public class C + { + protec[||]ted internal void goo(); + } + """, "protectedinternal"); + } + + [Fact] + public async Task TestPrivateProtected1() + { + await Test_KeywordAsync( + """ + public class C + { + private protec[||]ted void goo(); + } + """, "privateprotected"); + } + + [Fact] + public async Task TestPrivateProtected2() + { + await Test_KeywordAsync( + """ + public class C + { + priv[||]ate protected void goo(); + } + """, "privateprotected"); + } + + [Fact] + public async Task TestPrivateProtected3() + { + await Test_KeywordAsync( + """ + public class C + { + protected priv[||]ate void goo(); + } + """, "privateprotected"); + } + + [Fact] + public async Task TestPrivateProtected4() + { + await Test_KeywordAsync( + """ + public class C + { + prot[||]ected private void goo(); + } + """, "privateprotected"); + } + + [Fact] + public async Task TestModifierSoup() + { + await Test_KeywordAsync( +""" +public class C { private new prot[||]ected static unsafe void foo() { } -}", "privateprotected"); - } +} +""", "privateprotected"); + } + + [Fact] + public async Task TestModifierSoupField() + { + await Test_KeywordAsync( +""" +public class C +{ + new prot[||]ected static unsafe private goo; +} +""", "privateprotected"); + } + + [Fact] + public async Task TestVoid() + { + await Test_KeywordAsync( + """ + class C + { + vo[||]id goo() + { + } + } + """, "void"); + } + + [Fact] + public async Task TestReturn() + { + await Test_KeywordAsync( + """ + class C + { + void goo() + { + ret[||]urn; + } + } + """, "return"); + } + + [Fact] + public async Task TestClassPartialType() + { + await Test_KeywordAsync( + """ + part[||]ial class C + { + partial void goo(); + } + """, "partialtype"); + } + + [Fact] + public async Task TestRecordPartialType() + { + await Test_KeywordAsync( + """ + part[||]ial record C + { + partial void goo(); + } + """, "partialtype"); + } + + [Fact] + public async Task TestRecordWithPrimaryConstructorPartialType() + { + await Test_KeywordAsync( + """ + part[||]ial record C(string S) + { + partial void goo(); + } + """, "partialtype"); + } + + [Fact] + public async Task TestPartialMethodInClass() + { + await Test_KeywordAsync( + """ + partial class C + { + par[||]tial void goo(); + } + """, "partialmethod"); + } + + [Fact] + public async Task TestPartialMethodInRecord() + { + await Test_KeywordAsync( + """ + partial record C + { + par[||]tial void goo(); + } + """, "partialmethod"); + } + + [Fact] + public async Task TestExtendedPartialMethod() + { + await Test_KeywordAsync( + """ + partial class C + { + public par[||]tial void goo(); + } + """, "partialmethod"); + } + + [Fact] + public async Task TestWhereClause() + { + await Test_KeywordAsync( + """ + using System.Linq; + + class Program where T : class + { + void goo(string[] args) + { + var x = from a in args + whe[||]re a.Length > 0 + select a; + } + } + """, "whereclause"); + } + + [Fact] + public async Task TestWhereConstraint() + { + await Test_KeywordAsync( + """ + using System.Linq; + + class Program wh[||]ere T : class + { + void goo(string[] args) + { + var x = from a in args + where a.Length > 0 + select a; + } + } + """, "whereconstraint"); + } + + [Fact] + public async Task TestPreprocessor() + { + await TestAsync( + """ + #regi[||]on + #endregion + """, "#region"); + } + + [Fact] + public async Task TestPreprocessor2() + { + await TestAsync( + """ + #region[||] + #endregion + """, "#region"); + } + + [Fact] + public async Task TestConstructor() + { + await TestAsync( + """ + namespace N + { + class C + { + void goo() + { + var x = new [|C|](); + } + } + } + """, "N.C.#ctor"); + } + + [Fact] + public async Task TestGenericClass() + { + await TestAsync( + """ + namespace N + { + class C + { + void goo() + { + [|C|] c; + } + } + } + """, "N.C`1"); + } + + [Fact] + public async Task TestGenericMethod() + { + await TestAsync( + """ + namespace N + { + class C + { + void goo(T t, U u, V v) + { + C c; + c.g[|oo|](1, 1, 1); + } + } + } + """, "N.C`1.goo``3"); + } + + [Theory] + [InlineData("+")] + [InlineData("-")] + [InlineData("&")] + [InlineData("|")] + [InlineData("/")] + [InlineData("^")] + [InlineData(">")] + [InlineData(">=")] + [InlineData("!=")] + [InlineData("<")] + [InlineData("<=")] + [InlineData("<<")] + [InlineData(">>")] + [InlineData(">>>")] + [InlineData("*")] + [InlineData("%")] + [InlineData("&&")] + [InlineData("||")] + [InlineData("==")] + public async Task TestBinaryOperator(string operatorText) + { + await TestAsync( + $$""" + namespace N + { + class C + { + void goo() + { + var two = 1 [|{{operatorText}}|] 1; + } + } + } + """, $"{operatorText}_CSharpKeyword"); + } + + [Theory] + [InlineData("+=")] + [InlineData("-=")] + [InlineData("/=")] + [InlineData("*=")] + [InlineData("%=")] + [InlineData("&=")] + [InlineData("|=")] + [InlineData("^=")] + [InlineData("<<=")] + [InlineData(">>=")] + [InlineData(">>>=")] + public async Task TestCompoundOperator(string operatorText) + { + await TestAsync( + $$""" + namespace N + { + class C + { + void goo(int x) + { + x [|{{operatorText}}|] x; + } + } + } + """, $"{operatorText}_CSharpKeyword"); + } + + [Theory] + [InlineData("++")] + [InlineData("--")] + [InlineData("!")] + [InlineData("~")] + public async Task TestPrefixOperator(string operatorText) + { + await TestAsync( + $$""" + namespace N + { + class C + { + void goo(int x) + { + x = [|{{operatorText}}|]x; + } + } + } + """, $"{operatorText}_CSharpKeyword"); + } + + [Theory] + [InlineData("++")] + [InlineData("--")] + public async Task TestPostfixOperator(string operatorText) + { + await TestAsync( + $$""" + namespace N + { + class C + { + void goo(int x) + { + x = x[|{{operatorText}}|]; + } + } + } + """, $"{operatorText}_CSharpKeyword"); + } + + [Fact] + public async Task TestRelationalPattern() + { + await TestAsync( + """ + namespace N + { + class C + { + void goo(string x) + { + if (x is { Length: [||]> 5 }) { } + } + } + } + """, ">_CSharpKeyword"); + } + + [Fact] + public async Task TestGreaterThanInFunctionPointer() + { + await TestAsync(""" + unsafe class C + { + delegate*[||] f; + } + """, "functionPointer_CSharpKeyword"); + } + + [Fact] + public async Task TestLessThanInFunctionPointer() + { + await TestAsync(""" + unsafe class C + { + delegate*[||] f; + } + """, "functionPointer_CSharpKeyword"); + } + + [Fact] + public async Task TestEqualsOperatorInParameter() + { + await TestAsync( + """ + namespace N + { + class C + { + void goo(int x [|=|] 0) + { + } + } + } + """, "optionalParameter_CSharpKeyword"); + } + + [Fact] + public async Task TestEqualsOperatorInPropertyInitializer() + { + await TestAsync( + """ + namespace N + { + class C + { + int P { get; } [|=|] 5; + } + } + """, "propertyInitializer_CSharpKeyword"); + } + + [Fact] + public async Task TestVar() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + var[||] x = 3; + } + } + """, "var_CSharpKeyword"); + } + + [Fact] + public async Task TestEquals() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + var x =[||] 3; + } + } + """, "=_CSharpKeyword"); + } + + [Fact] + public async Task TestEqualsInEnum() + { + await TestAsync( + """ + enum E + { + A [||]= 1 + } + """, "enum_CSharpKeyword"); + } + + [Fact] + public async Task TestEqualsInAttribute() + { + await TestAsync( + """ + using System; + + [AttributeUsage(AttributeTargets.Class, Inherited [|=|] true)] + class MyAttribute : Attribute + { + } + """, "attributeNamedArgument_CSharpKeyword"); + } + + [Fact] + public async Task TestEqualsInUsingAlias() + { + await TestAsync( + """ + using SC [||]= System.Console; + """, "using_CSharpKeyword"); + } + + [Fact] + public async Task TestEqualsInAnonymousObjectMemberDeclarator() + { + await TestAsync( + """ + class C + { + void M() + { + var x = new { X [||]= 0 }; + } + } + """, "anonymousObject_CSharpKeyword"); + } + + [Fact] + public async Task TestEqualsInDocumentationComment() + { + await TestAsync( + """ + class C + { + /// + /// + /// + void M() + { + var x = new { X [||]= 0 }; + } + } + """, "see"); + } + + [Fact] + public async Task TestEqualsInLet() + { + await TestAsync( + """ + class C + { + void M() + { + var y = + from x1 in x2 + let x3 [||]= x4 + select x5; + } + } + """, "let_CSharpKeyword"); + } + + [Fact] + public async Task TestLetKeyword() + { + await TestAsync( + """ + class C + { + void M() + { + var y = + from x1 in x2 + [||]let x3 = x4 + select x5; + } + } + """, "let_CSharpKeyword"); + } + + [Fact] + public async Task TestFromIn() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + var x = from n i[||]n { + 1} + + select n + } + } + """, "from_CSharpKeyword"); + } + + [Fact] + public async Task TestProperty() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + new UriBuilder().Fragm[||]ent; + } + } + """, "System.UriBuilder.Fragment"); + } + + [Fact] + public async Task TestForeachIn() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + foreach (var x in[||] { + 1} ) + { + } + } + } + """, "in_CSharpKeyword"); + } + + [Fact] + public async Task TestRegionDescription() + { + await TestAsync( + """ + class Program + { + static void Main(string[] args) + { + #region Begin MyR[||]egion for testing + #endregion End + } + } + """, "#region"); + } + + [Fact] + public async Task TestGenericAngle_LessThanToken_TypeArgument() + { + await TestAsync( + """ + class Program + { + static void generic(T t) + { + generic[||](0); + } + } + """, "generics_CSharpKeyword"); + } + + [Fact] + public async Task TestGenericAngle_GreaterThanToken_TypeArgument() + { + await TestAsync( + """ + class Program + { + static void generic(T t) + { + generic|](0); + } + } + """, "generics_CSharpKeyword"); + } + + [Fact] + public async Task TestGenericAngle_LessThanToken_TypeParameter() + { + await TestAsync( + """ + class Program + { + static void generic[|<|]T>(T t) + { + generic(0); + } + } + """, "generics_CSharpKeyword"); + } + + [Fact] + public async Task TestGenericAngle_GreaterThanToken_TypeParameter() + { + await TestAsync( + """ + class Program + { + static void generic|](T t) + { + generic(0); + } + } + """, "generics_CSharpKeyword"); + } + + [Fact] + public async Task TestLocalReferenceIsType() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + int x; + x[||]; + } + } + """, "System.Int32"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/864266")] + public async Task TestConstantField() + { + await TestAsync( + """ + class Program + { + static void Main(string[] args) + { + var i = int.Ma[||]xValue; + } + } + """, "System.Int32.MaxValue"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862420")] + public async Task TestParameter() + { + await TestAsync( + """ + class Class2 + { + void M1(int par[||]ameter) // 1 + { + } + + void M2() + { + int argument = 1; + M1(parameter: argument); // 2 + } + } + """, "System.Int32"); + } + + [Fact] + public async Task TestRefReadonlyParameter_Ref() + { + await TestAsync( + """ + class C + { + void M(r[||]ef readonly int x) + { + } + } + """, "ref_CSharpKeyword"); + } + + [Fact] + public async Task TestRefReadonlyParameter_ReadOnly() + { + await TestAsync( + """ + class C + { + void M(ref read[||]only int x) + { + } + } + """, "readonly_CSharpKeyword"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862420")] + public async Task TestArgumentType() + { + await TestAsync( + """ + class Class2 + { + void M1(int pa[||]rameter) // 1 + { + } + + void M2() + { + int argument = 1; + M1(parameter: argument); // 2 + } + } + """, "System.Int32"); + } + + [Fact] + public async Task TestYieldReturn_OnYield() + { + await TestAsync(""" + using System.Collections.Generic; + + public class C + { + public IEnumerable M() + { + [|yield|] return 0; + } + } + """, "yield_CSharpKeyword"); + } + + [Fact] + public async Task TestYieldReturn_OnReturn() + { + await TestAsync(""" + using System.Collections.Generic; + + public class C + { + public IEnumerable M() + { + yield [|return|] 0; + } + } + """, "yield_CSharpKeyword"); + } + + [Fact] + public async Task TestYieldBreak_OnYield() + { + await TestAsync(""" + using System.Collections.Generic; + + public class C + { + public IEnumerable M() + { + [|yield|] break; + } + } + """, "yield_CSharpKeyword"); + } + + [Fact] + public async Task TestYieldBreak_OnBreak() + { + await TestAsync(""" + using System.Collections.Generic; + + public class C + { + public IEnumerable M() + { + yield [|break|] 0; + } + } + """, "yield_CSharpKeyword"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862396")] + public async Task TestNoToken() + { + await TestAsync( + """ + class Program + { + static void Main(string[] args) + { + } + }[||] + """, "vs.texteditor"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862328")] + public async Task TestLiteral() + { + await TestAsync( + """ + class Program + { + static void Main(string[] args) + { + Main(new string[] { "fo[||]o" }); + } + } + """, "System.String"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862478")] + public async Task TestColonColon() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + global:[||]:System.Console.Write("); + } + } + """, "::_CSharpKeyword"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46986")] + public async Task TestStringInterpolation() + { + await TestAsync( + """ + using System; + + class Program + { + static void Main(string[] args) + { + Console.WriteLine($[||]"Hello, {args[0]}"); + } + } + """, "$_CSharpKeyword"); + } + + [Fact] + public async Task TestUtf8String() + { + await TestAsync( + """ + using System; + + class Program + { + static void Main(string[] args) + { + var x = "Hel[||]lo"u8; + } + } + """, "Utf8StringLiteral_CSharpKeyword"); + } + + [Fact] + public async Task TestRawString() + { + await TestAsync( + """" + using System; + + class Program + { + static void Main(string[] args) + { + var x = """Hel[||]lo"""; + } + } + """", "RawStringLiteral_CSharpKeyword"); + } + + [Fact] + public async Task TestUtf8RawString() + { + await TestAsync( + """" + using System; + + class Program + { + static void Main(string[] args) + { + var x = """Hel[||]lo"""u8; + } + } + """", "Utf8StringLiteral_CSharpKeyword"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46986")] + public async Task TestVerbatimString() + { + await TestAsync( + """ + using System; + + class Program + { + static void Main(string[] args) + { + Console.WriteLine(@[||]"Hello\"); + } + } + """, "@_CSharpKeyword"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46986")] + public async Task TestVerbatimInterpolatedString1() + { + await TestAsync( + """ + using System; + + class Program + { + static void Main(string[] args) + { + Console.WriteLine(@[||]$"Hello\ {args[0]}"); + } + } + """, "@$_CSharpKeyword"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46986")] + public async Task TestVerbatimInterpolatedString2() + { + await TestAsync( + """ + using System; + + class Program + { + static void Main(string[] args) + { + Console.WriteLine($[||]@"Hello\ {args[0]}"); + } + } + """, "@$_CSharpKeyword"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/864658")] + public async Task TestNullable() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + int?[||] a = int.MaxValue; + a.Value.GetHashCode(); + } + } + """, "System.Nullable`1"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/863517")] + public async Task TestAfterLastToken() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + foreach (char var in "!!!")$$[||] + { + } + } + } + """, "vs.texteditor"); + } + + [Fact] + public async Task TestConditional() + { + await TestAsync( + """ + class Program + { + static void Main(string[] args) + { + var x = true [|?|] true : false; + } + } + """, "?_CSharpKeyword"); + } + + [Fact] + public async Task TestLocalVar() + { + await TestAsync( + """ + class C + { + void M() + { + var a = 0; + int v[||]ar = 1; + } + } + """, "System.Int32"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/867574")] + public async Task TestFatArrow() + { + await TestAsync( + """ + class C + { + void M() + { + var a = new System.Action(() =[||]> { + }); + } + } + """, "=>_CSharpKeyword"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/867572")] + public async Task TestSubscription() + { + await TestAsync( + """ + class CCC + { + event System.Action e; + + void M() + { + e +[||]= () => { + }; + } + } + """, "+=_CSharpKeyword"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/867554")] + public async Task TestComment() + { + await TestAsync(@"// some comm[||]ents here", "comments"); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/867529")] + public async Task TestDynamic() + { + await TestAsync( + """ + class C + { + void M() + { + dyna[||]mic d = 0; + } + } + """, "dynamic_CSharpKeyword"); + } + + [Fact] + public async Task TestRangeVariable() + { + await TestAsync( + """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class Program + { + static void Main(string[] args) + { + var zzz = from y in args + select [||]y; + } + } + """, "System.String"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36001")] + public async Task TestNameof() + { + await Test_KeywordAsync( + """ + class C + { + void goo() + { + var v = [||]nameof(goo); + } + } + """, "nameof"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46988")] + public async Task TestNullForgiving() + { + await Test_KeywordAsync( + """ + #nullable enable + class C + { + int goo(string? x) + { + return x[||]!.GetHashCode(); + } + } + """, "nullForgiving"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46988")] + public async Task TestLogicalNot() + { + await Test_KeywordAsync( + """ + class C + { + bool goo(bool x) + { + return [||]!x; + } + } + """, "!"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultSwitchCase() + { + await Test_KeywordAsync( + """ + class C + { + void M1(int parameter) + { + switch(parameter) { + defa[||]ult: + parameter = default; + break; + } + } + } + """, "defaultcase"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultLiteralExpressionInsideSwitch() + { + await Test_KeywordAsync( + """ + class C + { + void M1(int parameter) + { + switch(parameter) { + default: + parameter = defa[||]ult; + break; + } + } + } + """, "default"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultExpressionInsideSwitch() + { + await Test_KeywordAsync( + """ + class C + { + void M1(int parameter) + { + switch(parameter) { + default: + parameter = defa[||]ult(int); + break; + } + } + } + """, "default"); + } - [Fact] - public async Task TestModifierSoupField() - { - await Test_KeywordAsync( - @"public class C -{ - new prot[||]ected static unsafe private goo; -}", "privateprotected"); - } - - [Fact] - public async Task TestVoid() - { - await Test_KeywordAsync( -@"class C -{ - vo[||]id goo() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultLiteralExpression() { + await Test_KeywordAsync( + """ + class C + { + int field = defa[||]ult; + } + """, "default"); } -}", "void"); - } - [Fact] - public async Task TestReturn() - { - await Test_KeywordAsync( -@"class C -{ - void goo() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultExpression() { - ret[||]urn; + await Test_KeywordAsync( + """ + class C + { + int field = defa[||]ult(int); + } + """, "default"); } -}", "return"); - } - - [Fact] - public async Task TestClassPartialType() - { - await Test_KeywordAsync( -@"part[||]ial class C -{ - partial void goo(); -}", "partialtype"); - } - - [Fact] - public async Task TestRecordPartialType() - { - await Test_KeywordAsync( -@"part[||]ial record C -{ - partial void goo(); -}", "partialtype"); - } - - [Fact] - public async Task TestRecordWithPrimaryConstructorPartialType() - { - await Test_KeywordAsync( -@"part[||]ial record C(string S) -{ - partial void goo(); -}", "partialtype"); - } - - [Fact] - public async Task TestPartialMethodInClass() - { - await Test_KeywordAsync( -@"partial class C -{ - par[||]tial void goo(); -}", "partialmethod"); - } - - [Fact] - public async Task TestPartialMethodInRecord() - { - await Test_KeywordAsync( -@"partial record C -{ - par[||]tial void goo(); -}", "partialmethod"); - } - - [Fact] - public async Task TestExtendedPartialMethod() - { - await Test_KeywordAsync( -@"partial class C -{ - public par[||]tial void goo(); -}", "partialmethod"); - } - [Fact] - public async Task TestWhereClause() - { - await Test_KeywordAsync( -@"using System.Linq; - -class Program where T : class -{ - void goo(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultLiteralExpressionInOptionalParameter() { - var x = from a in args - whe[||]re a.Length > 0 - select a; + await Test_KeywordAsync( + """ + class C + { + void M1(int parameter = defa[||]ult) { + } + } + """, "default"); } -}", "whereclause"); - } - - [Fact] - public async Task TestWhereConstraint() - { - await Test_KeywordAsync( -@"using System.Linq; -class Program wh[||]ere T : class -{ - void goo(string[] args) - { - var x = from a in args - where a.Length > 0 - select a; - } -}", "whereconstraint"); - } - - [Fact] - public async Task TestPreprocessor() - { - await TestAsync( -@"#regi[||]on -#endregion", "#region"); - } - - [Fact] - public async Task TestPreprocessor2() - { - await TestAsync( -@"#region[||] -#endregion", "#region"); - } - - [Fact] - public async Task TestConstructor() - { - await TestAsync( -@"namespace N -{ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultExpressionInOptionalParameter() { - void goo() - { - var x = new [|C|](); - } + await Test_KeywordAsync( + """ + class C + { + void M1(int parameter = defa[||]ult(int)) { + } + } + """, "default"); } -}", "N.C.#ctor"); - } - [Fact] - public async Task TestGenericClass() - { - await TestAsync( -@"namespace N -{ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultLiteralExpressionInMethodCall() { - void goo() - { - [|C|] c; - } + await Test_KeywordAsync( + """ + class C + { + void M1() { + M2(defa[||]ult); + } + } + """, "default"); } -}", "N.C`1"); - } - [Fact] - public async Task TestGenericMethod() - { - await TestAsync( -@"namespace N -{ - class C - { - void goo(T t, U u, V v) - { - C c; - c.g[|oo|](1, 1, 1); - } - } -}", "N.C`1.goo``3"); - } - - [Theory] - [InlineData("+")] - [InlineData("-")] - [InlineData("&")] - [InlineData("|")] - [InlineData("/")] - [InlineData("^")] - [InlineData(">")] - [InlineData(">=")] - [InlineData("!=")] - [InlineData("<")] - [InlineData("<=")] - [InlineData("<<")] - [InlineData(">>")] - [InlineData(">>>")] - [InlineData("*")] - [InlineData("%")] - [InlineData("&&")] - [InlineData("||")] - [InlineData("==")] - public async Task TestBinaryOperator(string operatorText) - { - await TestAsync( -$@"namespace N -{{ - class C - {{ - void goo() - {{ - var two = 1 [|{operatorText}|] 1; - }} - }} -}}", $"{operatorText}_CSharpKeyword"); - } - - [Theory] - [InlineData("+=")] - [InlineData("-=")] - [InlineData("/=")] - [InlineData("*=")] - [InlineData("%=")] - [InlineData("&=")] - [InlineData("|=")] - [InlineData("^=")] - [InlineData("<<=")] - [InlineData(">>=")] - [InlineData(">>>=")] - public async Task TestCompoundOperator(string operatorText) - { - await TestAsync( -$@"namespace N -{{ - class C - {{ - void goo(int x) - {{ - x [|{operatorText}|] x; - }} - }} -}}", $"{operatorText}_CSharpKeyword"); - } - - [Theory] - [InlineData("++")] - [InlineData("--")] - [InlineData("!")] - [InlineData("~")] - public async Task TestPrefixOperator(string operatorText) - { - await TestAsync( -$@"namespace N -{{ - class C - {{ - void goo(int x) - {{ - x = [|{operatorText}|]x; - }} - }} -}}", $"{operatorText}_CSharpKeyword"); - } - - [Theory] - [InlineData("++")] - [InlineData("--")] - public async Task TestPostfixOperator(string operatorText) - { - await TestAsync( -$@"namespace N -{{ - class C - {{ - void goo(int x) - {{ - x = x[|{operatorText}|]; - }} - }} -}}", $"{operatorText}_CSharpKeyword"); - } - - [Fact] - public async Task TestRelationalPattern() - { - await TestAsync( -@"namespace N -{ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestDefaultExpressionInMethodCall() { - void goo(string x) - { - if (x is { Length: [||]> 5 }) { } - } + await Test_KeywordAsync( + """ + class C + { + void M1() { + M2(defa[||]ult(int)); + } + } + """, "default"); } -}", ">_CSharpKeyword"); - } - [Fact] - public async Task TestGreaterThanInFunctionPointer() - { - await TestAsync(@" -unsafe class C -{ - delegate*[||] f; -} -", "functionPointer_CSharpKeyword"); - } - - [Fact] - public async Task TestLessThanInFunctionPointer() - { - await TestAsync(@" -unsafe class C -{ - delegate*[||] f; -} -", "functionPointer_CSharpKeyword"); - } - - [Fact] - public async Task TestEqualsOperatorInParameter() - { - await TestAsync( -@"namespace N -{ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestOuterClassDeclaration() { - void goo(int x [|=|] 0) - { - } + await Test_KeywordAsync( + """ + cla[||]ss OuterClass where T : class + { + class InnerClass where T : class { } + } + """, "class"); } -}", "optionalParameter_CSharpKeyword"); - } - [Fact] - public async Task TestEqualsOperatorInPropertyInitializer() - { - await TestAsync( -@"namespace N -{ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestInnerClassDeclaration() { - int P { get; } [|=|] 5; + await Test_KeywordAsync( + """ + class OuterClass where T : class + { + cla[||]ss InnerClass where T : class { } + } + """, "class"); } -}", "propertyInitializer_CSharpKeyword"); - } - [Fact] - public async Task TestVar() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestClassConstraintInOuterClass() + { + await Test_KeywordAsync( + """ + class OuterClass where T : cla[||]ss + { + class InnerClass where T : class { } + } + """, "classconstraint"); + } -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestClassConstraintInInnerClass() { - var[||] x = 3; + await Test_KeywordAsync( + """ + class OuterClass where T : class + { + class InnerClass where T : cla[||]ss { } + } + """, "classconstraint"); } -}", "var_CSharpKeyword"); - } - [Fact] - public async Task TestEquals() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestClassConstraintInGenericMethod() + { + await Test_KeywordAsync( + """ + class C + { + void M1() where T : cla[||]ss { } + } + """, "classconstraint"); + } -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestClassConstraintInGenericDelegate() { - var x =[||] 3; + await Test_KeywordAsync( + """ + class C + { + delegate T MyDelegate() where T : cla[||]ss; + } + """, "classconstraint"); } -}", "=_CSharpKeyword"); - } - [Fact] - public async Task TestEqualsInEnum() - { - await TestAsync( -@" -enum E -{ - A [||]= 1 -}", "enum_CSharpKeyword"); - } - - [Fact] - public async Task TestEqualsInAttribute() - { - await TestAsync( -@" -using System; - -[AttributeUsage(AttributeTargets.Class, Inherited [|=|] true)] -class MyAttribute : Attribute -{ -} -", "attributeNamedArgument_CSharpKeyword"); - } - - [Fact] - public async Task TestEqualsInUsingAlias() - { - await TestAsync( -@" -using SC [||]= System.Console; -", "using_CSharpKeyword"); - } - - [Fact] - public async Task TestEqualsInAnonymousObjectMemberDeclarator() - { - await TestAsync( -@" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestOuterStructDeclaration() { - var x = new { X [||]= 0 }; + await Test_KeywordAsync( + """ + str[||]uct OuterStruct where T : struct + { + struct InnerStruct where T : struct { } + } + """, "struct"); } -} -", "anonymousObject_CSharpKeyword"); - } - - [Fact] - public async Task TestEqualsInDocumentationComment() - { - await TestAsync( -@" -class C -{ - /// - /// - /// - void M() + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestInnerStructDeclaration() { - var x = new { X [||]= 0 }; + await Test_KeywordAsync( + """ + struct OuterStruct where T : struct + { + str[||]uct InnerStruct where T : struct { } + } + """, "struct"); } -} -", "see"); - } - - [Fact] - public async Task TestEqualsInLet() - { - await TestAsync( -@" -class C -{ - void M() + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestStructConstraintInOuterStruct() { - var y = - from x1 in x2 - let x3 [||]= x4 - select x5; + await Test_KeywordAsync( + """ + struct OuterStruct where T : str[||]uct + { + struct InnerStruct where T : struct { } + } + """, "structconstraint"); } -} -", "let_CSharpKeyword"); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestStructConstraintInInnerStruct() + { + await Test_KeywordAsync( + """ + struct OuterStruct where T : struct + { + struct InnerStruct where T : str[||]uct { } + } + """, "structconstraint"); + } - [Fact] - public async Task TestLetKeyword() - { - await TestAsync( -@" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestStructConstraintInGenericMethod() { - var y = - from x1 in x2 - [||]let x3 = x4 - select x5; + await Test_KeywordAsync( + """ + struct C + { + void M1() where T : str[||]uct { } + } + """, "structconstraint"); } -} -", "let_CSharpKeyword"); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestStructConstraintInGenericDelegate() + { + await Test_KeywordAsync( + """ + struct C + { + delegate T MyDelegate() where T : str[||]uct; + } + """, "structconstraint"); + } - [Fact] - public async Task TestFromIn() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestAllowsRefStructAntiConstraint() + { + await Test_KeywordAsync( + """ + class C + { + void M() + where T : all[||]ows ref struct + { + } + } + """, "allows"); + } -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestUsingStaticOnUsingKeyword() { - var x = from n i[||]n { - 1} + await Test_KeywordAsync( + """ + us[||]ing static namespace.Class; - select n - } -}", "from_CSharpKeyword"); - } + static class C + { + static int Field; - [Fact] - public async Task TestProperty() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; + static void Method() {} + } + """, "using-static"); + } -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestNormalUsingDirective() { - new UriBuilder().Fragm[||]ent; - } -}", "System.UriBuilder.Fragment"); - } + await Test_KeywordAsync( + """ + us[||]ing namespace.Class; - [Fact] - public async Task TestForeachIn() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; + static class C + { + static int Field; -class Program -{ - static void Main(string[] args) - { - foreach (var x in[||] { - 1} ) - { - } + static void Method() {} + } + """, "using"); } -}", "in_CSharpKeyword"); - } - [Fact] - public async Task TestRegionDescription() - { - await TestAsync( -@"class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestUsingStatement() { - #region Begin MyR[||]egion for testing - #endregion End + await Test_KeywordAsync( + """ + using namespace.Class; + + class C + { + void Method(String someString) { + us[||]ing (var reader = new StringReader(someString)) + { + } + } + } + """, "using-statement"); } -}", "#region"); - } - [Fact] - public async Task TestGenericAngle_LessThanToken_TypeArgument() - { - await TestAsync( -@"class Program -{ - static void generic(T t) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestUsingDeclaration() { - generic[||](0); + await Test_KeywordAsync( + """ + using namespace.Class; + + class C + { + void Method(String someString) { + us[||]ing var reader = new StringReader(someString); + } + } + """, "using-statement"); } -}", "generics_CSharpKeyword"); - } - [Fact] - public async Task TestGenericAngle_GreaterThanToken_TypeArgument() - { - await TestAsync( -@"class Program -{ - static void generic(T t) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestUsingStaticOnStaticKeyword() { - generic|](0); + await Test_KeywordAsync( + """ + using sta[||]tic namespace.Class; + + static class C + { + static int Field; + + static void Method() {} + } + """, "using-static"); } -}", "generics_CSharpKeyword"); - } - [Fact] - public async Task TestGenericAngle_LessThanToken_TypeParameter() - { - await TestAsync( -@"class Program -{ - static void generic[|<|]T>(T t) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestStaticClass() { - generic(0); + await Test_KeywordAsync( + """ + using static namespace.Class; + + sta[||]tic class C + { + static int Field; + + static void Method() {} + } + """, "static"); } -}", "generics_CSharpKeyword"); - } - [Fact] - public async Task TestGenericAngle_GreaterThanToken_TypeParameter() - { - await TestAsync( -@"class Program -{ - static void generic|](T t) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestStaticField() { - generic(0); - } -}", "generics_CSharpKeyword"); - } + await Test_KeywordAsync( + """ + using static namespace.Class; - [Fact] - public async Task TestLocalReferenceIsType() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; + static class C + { + sta[||]tic int Field; -class Program -{ - static void Main(string[] args) - { - int x; - x[||]; + static void Method() {} + } + """, "static"); } -}", "System.Int32"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/864266")] - public async Task TestConstantField() - { - await TestAsync( -@"class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] + public async Task TestStaticMethod() { - var i = int.Ma[||]xValue; + await Test_KeywordAsync( + """ + using static namespace.Class; + + static class C + { + static int Field; + + sta[||]tic void Method() {} + } + """, "static"); } -}", "System.Int32.MaxValue"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862420")] - public async Task TestParameter() - { - await TestAsync( -@"class Class2 -{ - void M1(int par[||]ameter) // 1 + [Fact] + public async Task TestWithKeyword() { + await Test_KeywordAsync( + """ + public record Point(int X, int Y); + + public static class Program + { + public static void Main() + { + var p1 = new Point(0, 0); + var p2 = p1 w[||]ith { X = 5 }; + } + } + """, "with"); } - void M2() + [Fact] + public async Task TestDiscard() { - int argument = 1; - M1(parameter: argument); // 2 + await Test_KeywordAsync( + """ + class C + { + void M() + { + [||]_ = Goo(); + } + + object Goo() => null; + } + """, "discard"); } -}", "System.Int32"); - } - [Fact] - public async Task TestRefReadonlyParameter_Ref() - { - await TestAsync( - """ - class C + [Fact] + public async Task TestChecked_01() + { + await Test_KeywordAsync( + """ + public class C + { + void goo() { - void M(r[||]ef readonly int x) + chec[||]ked { } } - """, "ref_CSharpKeyword"); - } + } + """, "checked"); + } - [Fact] - public async Task TestRefReadonlyParameter_ReadOnly() - { - await TestAsync( - """ - class C + [Fact] + public async Task TestChecked_02() + { + await Test_KeywordAsync( + """ + public class C + { + int goo() { - void M(ref read[||]only int x) - { - } + return chec[||]ked(0); } - """, "readonly_CSharpKeyword"); - } + } + """, "checked"); + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862420")] - public async Task TestArgumentType() - { - await TestAsync( -@"class Class2 -{ - void M1(int pa[||]rameter) // 1 + [Fact] + public async Task TestChecked_03() { + await Test_KeywordAsync( + """ + public class C + { + C operator chec[||]ked -(C x) {} + } + """, "checked"); } - void M2() + [Fact] + public async Task TestChecked_04() { - int argument = 1; - M1(parameter: argument); // 2 + await Test_KeywordAsync( + """ + public class C + { + C operator chec[||]ked +(C x, C y) {} + } + """, "checked"); } -}", "System.Int32"); - } - - [Fact] - public async Task TestYieldReturn_OnYield() - { - await TestAsync(@" -using System.Collections.Generic; -public class C -{ - public IEnumerable M() + [Fact] + public async Task TestChecked_05() { - [|yield|] return 0; + await Test_KeywordAsync( + """ + public class C + { + explicit operator chec[||]ked string(C x) {} + } + """, "checked"); } -} -", "yield_CSharpKeyword"); - } - - [Fact] - public async Task TestYieldReturn_OnReturn() - { - await TestAsync(@" -using System.Collections.Generic; -public class C -{ - public IEnumerable M() + [Fact] + public async Task TestChecked_06() { - yield [|return|] 0; + await Test_KeywordAsync( + """ + public class C + { + C I1.operator chec[||]ked -(C x) {} + } + """, "checked"); } -} -", "yield_CSharpKeyword"); - } - - [Fact] - public async Task TestYieldBreak_OnYield() - { - await TestAsync(@" -using System.Collections.Generic; -public class C -{ - public IEnumerable M() + [Fact] + public async Task TestChecked_07() { - [|yield|] break; + await Test_KeywordAsync( + """ + public class C + { + C I1.operator chec[||]ked +(C x, C y) {} + } + """, "checked"); } -} -", "yield_CSharpKeyword"); - } - - [Fact] - public async Task TestYieldBreak_OnBreak() - { - await TestAsync(@" -using System.Collections.Generic; -public class C -{ - public IEnumerable M() + [Fact] + public async Task TestChecked_08() { - yield [|break|] 0; + await Test_KeywordAsync( + """ + public class C + { + explicit I1.operator chec[||]ked string(C x) {} + } + """, "checked"); } -} -", "yield_CSharpKeyword"); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862396")] - public async Task TestNoToken() - { - await TestAsync( -@"class Program -{ - static void Main(string[] args) + + [Fact] + public async Task TestChecked_09() { + await Test_KeywordAsync( + """ + public class C + { + /// + /// + /// + void goo() + { + } + } + """, "checked"); } -}[||]", "vs.texteditor"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862328")] - public async Task TestLiteral() - { - await TestAsync( -@"class Program -{ - static void Main(string[] args) + [Fact] + public async Task TestChecked_10() { - Main(new string[] { ""fo[||]o"" }); + await Test_KeywordAsync( + """ + public class C + { + /// + /// + /// + void goo() + { + } + } + """, "checked"); } -}", "System.String"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/862478")] - public async Task TestColonColon() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -class Program -{ - static void Main(string[] args) + [Fact] + public async Task TestChecked_11() { - global:[||]:System.Console.Write(""); + await Test_KeywordAsync( + """ + public class C + { + /// + /// + /// + void goo() + { + } + } + """, "checked"); } -}", "::_CSharpKeyword"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46986")] - public async Task TestStringInterpolation() - { - await TestAsync( -@"using System; - -class Program -{ - static void Main(string[] args) + [Fact] + public async Task TestRequired() { - Console.WriteLine($[||]""Hello, {args[0]}""); + await Test_KeywordAsync(""" + public class C + { + re[||]quired int Field; + } + """, "required"); } -}", "$_CSharpKeyword"); - } - [Fact] - public async Task TestUtf8String() - { - await TestAsync( -@"using System; - -class Program -{ - static void Main(string[] args) + [Fact] + public async Task TestDefaultConstraint() { - var x = ""Hel[||]lo""u8; + await Test_KeywordAsync(""" + public class Base + { + virtual void M(T? t) { } + } + public class C + { + override void M() where T : def[||]ault { } + } + """, expectedText: "defaultconstraint"); } -}", "Utf8StringLiteral_CSharpKeyword"); - } - [Fact] - public async Task TestRawString() - { - await TestAsync( -@"using System; - -class Program -{ - static void Main(string[] args) + [Fact] + public async Task TestDefaultCase() { - var x = """"""Hel[||]lo""""""; + await Test_KeywordAsync(""" + public class C + { + void M(object o) + { + switch (o) + { + case 1: + goto def[||]ault; + default: + return; + } + } + } + """, expectedText: "defaultcase"); } -}", "RawStringLiteral_CSharpKeyword"); - } - [Fact] - public async Task TestUtf8RawString() - { - await TestAsync( -@"using System; - -class Program -{ - static void Main(string[] args) + [Fact] + public async Task TestGotoDefault() { - var x = """"""Hel[||]lo""""""u8; + await Test_KeywordAsync(""" + public class C + { + void M(object o) + { + switch (o) + { + case 1: + goto default; + def[||]ault: + return; + } + } + } + """, expectedText: "defaultcase"); } -}", "Utf8StringLiteral_CSharpKeyword"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46986")] - public async Task TestVerbatimString() - { - await TestAsync( -@"using System; - -class Program -{ - static void Main(string[] args) + [Fact] + public async Task TestLineDefault() { - Console.WriteLine(@[||]""Hello\""); + await Test_KeywordAsync(""" + #line def[||]ault + """, expectedText: "defaultline"); } -}", "@_CSharpKeyword"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46986")] - public async Task TestVerbatimInterpolatedString1() - { - await TestAsync( -@"using System; - -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] + public async Task TestNotnull_OnType() { - Console.WriteLine(@[||]$""Hello\ {args[0]}""); + await Test_KeywordAsync(""" + public class C where T : not[||]null + { + } + """, expectedText: "notnull"); } -}", "@$_CSharpKeyword"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46986")] - public async Task TestVerbatimInterpolatedString2() - { - await TestAsync( -@"using System; - -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] + public async Task TestNotnull_OnMethod() { - Console.WriteLine($[||]@""Hello\ {args[0]}""); + await Test_KeywordAsync(""" + public class C + { + void M() where T : not[||]null + { + } + } + """, expectedText: "notnull"); } -}", "@$_CSharpKeyword"); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/864658")] - public async Task TestNullable() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] + public async Task TestNotnull_FieldName() { - int?[||] a = int.MaxValue; - a.Value.GetHashCode(); + await TestAsync(""" + public class C + { + int not[||]null = 0; + } + """, expectedText: "C.notnull"); } -}", "System.Nullable`1"); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/863517")] - public async Task TestAfterLastToken() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] + public async Task TestUnmanaged_OnType() { - foreach (char var in ""!!!"")$$[||] - { - } + await Test_KeywordAsync(""" + public class C where T : un[||]managed + { + } + """, expectedText: "unmanaged"); } -}", "vs.texteditor"); - } - [Fact] - public async Task TestConditional() - { - await TestAsync( -@"class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] + public async Task TestUnmanaged_OnMethod() { - var x = true [|?|] true : false; + await Test_KeywordAsync(""" + public class C + { + void M() where T : un[||]managed + { + } + } + """, expectedText: "unmanaged"); } -}", "?_CSharpKeyword"); - } - [Fact] - public async Task TestLocalVar() - { - await TestAsync( -@"class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] + public async Task TestUnmanaged_LocalName() { - var a = 0; - int v[||]ar = 1; + await TestAsync(""" + int un[||]managed = 0; + """, expectedText: "System.Int32"); } -}", "System.Int32"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/867574")] - public async Task TestFatArrow() - { - await TestAsync( -@"class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65312")] + public async Task TestSwitchStatement() { - var a = new System.Action(() =[||]> { - }); + await Test_KeywordAsync(""" + swit[||]ch (1) { default: break; } + """, expectedText: "switch"); } -}", "=>_CSharpKeyword"); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/867572")] - public async Task TestSubscription() - { - await TestAsync( -@"class CCC -{ - event System.Action e; - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65312")] + public async Task TestSwitchExpression() { - e +[||]= () => { - }; + await Test_KeywordAsync(""" + _ = 1 swit[||]ch { _ => 0 }; + """, expectedText: "switch-expression"); } -}", "+=_CSharpKeyword"); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/867554")] - public async Task TestComment() - { - await TestAsync(@"// some comm[||]ents here", "comments"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/867529")] - public async Task TestDynamic() - { - await TestAsync( -@"class C -{ - void M() + [Fact] + public async Task TestFile() { - dyna[||]mic d = 0; + await Test_KeywordAsync(""" + fi[||]le class C { } + """, expectedText: "file"); } -}", "dynamic_CSharpKeyword"); - } - - [Fact] - public async Task TestRangeVariable() - { - await TestAsync( -@"using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -class Program -{ - static void Main(string[] args) + [Fact] + public async Task TestRightShift() { - var zzz = from y in args - select [||]y; + await Test_KeywordAsync(""" + _ = 1 >[||]> 2; + """, expectedText: ">>"); } -}", "System.String"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36001")] - public async Task TestNameof() - { - await Test_KeywordAsync( -@"class C -{ - void goo() + [Fact] + public async Task TestUnsignedRightShift() { - var v = [||]nameof(goo); + await Test_KeywordAsync(""" + _ = 1 >>[||]> 2; + """, expectedText: ">>>"); } -}", "nameof"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46988")] - public async Task TestNullForgiving() - { - await Test_KeywordAsync( -@"#nullable enable -class C -{ - int goo(string? x) + [Fact] + public async Task TestUnsignedRightShiftAssignment() { - return x[||]!.GetHashCode(); + await Test_KeywordAsync(""" + 1 >>[||]>= 2; + """, expectedText: ">>>="); } -}", "nullForgiving"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/46988")] - public async Task TestLogicalNot() - { - await Test_KeywordAsync( -@"class C -{ - bool goo(bool x) + [Fact] + public async Task TestPreprocessorIf() { - return [||]!x; + await TestAsync( + """ + #i[||]f ANY + #endif + """, "#if"); } -}", "!"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultSwitchCase() - { - await Test_KeywordAsync( -@"class C -{ - void M1(int parameter) - { - switch(parameter) { - defa[||]ult: - parameter = default; - break; - } - } -}", "defaultcase"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultLiteralExpressionInsideSwitch() - { - await Test_KeywordAsync( -@"class C -{ - void M1(int parameter) - { - switch(parameter) { - default: - parameter = defa[||]ult; - break; - } - } -}", "default"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultExpressionInsideSwitch() - { - await Test_KeywordAsync( -@"class C -{ - void M1(int parameter) - { - switch(parameter) { - default: - parameter = defa[||]ult(int); - break; - } - } -}", "default"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultLiteralExpression() - { - await Test_KeywordAsync( -@"class C -{ - int field = defa[||]ult; -}", "default"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultExpression() - { - await Test_KeywordAsync( -@"class C -{ - int field = defa[||]ult(int); -}", "default"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultLiteralExpressionInOptionalParameter() - { - await Test_KeywordAsync( -@"class C -{ - void M1(int parameter = defa[||]ult) { + [Fact] + public async Task TestPreprocessorIf2() + { + await TestAsync( + """ + #if ANY[||] + #endif + """, "#if"); } -}", "default"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultExpressionInOptionalParameter() - { - await Test_KeywordAsync( -@"class C -{ - void M1(int parameter = defa[||]ult(int)) { + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorIf3() + { + await TestAsync( + """ + #if ANY[||]THING + #endif + """, "#if"); } -}", "default"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultLiteralExpressionInMethodCall() - { - await Test_KeywordAsync( -@"class C -{ - void M1() { - M2(defa[||]ult); + [Fact] + public async Task TestPreprocessorEndIf() + { + await TestAsync( + """ + #if ANY + #en[||]dif + """, "#endif"); } -}", "default"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestDefaultExpressionInMethodCall() - { - await Test_KeywordAsync( -@"class C -{ - void M1() { - M2(defa[||]ult(int)); - } -}", "default"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestOuterClassDeclaration() - { - await Test_KeywordAsync( -@"cla[||]ss OuterClass where T : class -{ - class InnerClass where T : class { } -}", "class"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestInnerClassDeclaration() - { - await Test_KeywordAsync( -@"class OuterClass where T : class -{ - cla[||]ss InnerClass where T : class { } -}", "class"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestClassConstraintInOuterClass() - { - await Test_KeywordAsync( -@"class OuterClass where T : cla[||]ss -{ - class InnerClass where T : class { } -}", "classconstraint"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestClassConstraintInInnerClass() - { - await Test_KeywordAsync( -@"class OuterClass where T : class -{ - class InnerClass where T : cla[||]ss { } -}", "classconstraint"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestClassConstraintInGenericMethod() - { - await Test_KeywordAsync( -@"class C -{ - void M1() where T : cla[||]ss { } -}", "classconstraint"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestClassConstraintInGenericDelegate() - { - await Test_KeywordAsync( -@"class C -{ - delegate T MyDelegate() where T : cla[||]ss; -}", "classconstraint"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestOuterStructDeclaration() - { - await Test_KeywordAsync( -@"str[||]uct OuterStruct where T : struct -{ - struct InnerStruct where T : struct { } -}", "struct"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestInnerStructDeclaration() - { - await Test_KeywordAsync( -@"struct OuterStruct where T : struct -{ - str[||]uct InnerStruct where T : struct { } -}", "struct"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestStructConstraintInOuterStruct() - { - await Test_KeywordAsync( -@"struct OuterStruct where T : str[||]uct -{ - struct InnerStruct where T : struct { } -}", "structconstraint"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestStructConstraintInInnerStruct() - { - await Test_KeywordAsync( -@"struct OuterStruct where T : struct -{ - struct InnerStruct where T : str[||]uct { } -}", "structconstraint"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestStructConstraintInGenericMethod() - { - await Test_KeywordAsync( -@"struct C -{ - void M1() where T : str[||]uct { } -}", "structconstraint"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestStructConstraintInGenericDelegate() - { - await Test_KeywordAsync( -@"struct C -{ - delegate T MyDelegate() where T : str[||]uct; -}", "structconstraint"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestAllowsRefStructAntiConstraint() - { - await Test_KeywordAsync( -@"class C -{ - void M() - where T : all[||]ows ref struct - { - } -}", "allows"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestUsingStaticOnUsingKeyword() - { - await Test_KeywordAsync( -@"us[||]ing static namespace.Class; - -static class C -{ - static int Field; - - static void Method() {} -}", "using-static"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestNormalUsingDirective() - { - await Test_KeywordAsync( -@"us[||]ing namespace.Class; - -static class C -{ - static int Field; - - static void Method() {} -}", "using"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestUsingStatement() - { - await Test_KeywordAsync( -@"using namespace.Class; - -class C -{ - void Method(String someString) { - us[||]ing (var reader = new StringReader(someString)) - { - } - } -}", "using-statement"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestUsingDeclaration() - { - await Test_KeywordAsync( -@"using namespace.Class; - -class C -{ - void Method(String someString) { - us[||]ing var reader = new StringReader(someString); - } -}", "using-statement"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestUsingStaticOnStaticKeyword() - { - await Test_KeywordAsync( -@"using sta[||]tic namespace.Class; - -static class C -{ - static int Field; - - static void Method() {} -}", "using-static"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestStaticClass() - { - await Test_KeywordAsync( -@"using static namespace.Class; - -sta[||]tic class C -{ - static int Field; - - static void Method() {} -}", "static"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestStaticField() - { - await Test_KeywordAsync( -@"using static namespace.Class; - -static class C -{ - sta[||]tic int Field; - - static void Method() {} -}", "static"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48392")] - public async Task TestStaticMethod() - { - await Test_KeywordAsync( -@"using static namespace.Class; - -static class C -{ - static int Field; - - sta[||]tic void Method() {} -}", "static"); - } - - [Fact] - public async Task TestWithKeyword() - { - await Test_KeywordAsync( -@" -public record Point(int X, int Y); - -public static class Program -{ - public static void Main() - { - var p1 = new Point(0, 0); - var p2 = p1 w[||]ith { X = 5 }; - } -}", "with"); - } - - [Fact] - public async Task TestDiscard() - { - await Test_KeywordAsync( -@" -class C -{ - void M() - { - [||]_ = Goo(); - } - - object Goo() => null; -}", "discard"); - } - - [Fact] - public async Task TestNotFound() - { - await TestAsync( -@" -#if ANY[||]THING -#endif -", "vs.texteditor"); - } - - [Fact] - public async Task TestChecked_01() - { - await Test_KeywordAsync( -@"public class C -{ - void goo() + [Fact] + public async Task TestPreprocessorEndIf2() { - chec[||]ked - { - } + await TestAsync( + """ + #if ANY + #endif[||] + + """, "#endif"); } -}", "checked"); - } - [Fact] - public async Task TestChecked_02() - { - await Test_KeywordAsync( -@"public class C -{ - int goo() + [Fact] + public async Task TestPreprocessorElse() { - return chec[||]ked(0); + await TestAsync( + """ + #if ANY + #el[||]se + #endif + """, "#else"); } -}", "checked"); - } - [Fact] - public async Task TestChecked_03() - { - await Test_KeywordAsync( -@"public class C -{ - C operator chec[||]ked -(C x) {} -}", "checked"); - } - - [Fact] - public async Task TestChecked_04() - { - await Test_KeywordAsync( -@"public class C -{ - C operator chec[||]ked +(C x, C y) {} -}", "checked"); - } - - [Fact] - public async Task TestChecked_05() - { - await Test_KeywordAsync( -@"public class C -{ - explicit operator chec[||]ked string(C x) {} -}", "checked"); - } - - [Fact] - public async Task TestChecked_06() - { - await Test_KeywordAsync( -@"public class C -{ - C I1.operator chec[||]ked -(C x) {} -}", "checked"); - } - - [Fact] - public async Task TestChecked_07() - { - await Test_KeywordAsync( -@"public class C -{ - C I1.operator chec[||]ked +(C x, C y) {} -}", "checked"); - } - - [Fact] - public async Task TestChecked_08() - { - await Test_KeywordAsync( -@"public class C -{ - explicit I1.operator chec[||]ked string(C x) {} -}", "checked"); - } - - [Fact] - public async Task TestChecked_09() - { - await Test_KeywordAsync( -@"public class C -{ - /// - /// - /// - void goo() + [Fact] + public async Task TestPreprocessorElse2() { + await TestAsync( + """ + #if ANY + #else[||] + #endif + """, "#else"); } -}", "checked"); - } - [Fact] - public async Task TestChecked_10() - { - await Test_KeywordAsync( -@"public class C -{ - /// - /// - /// - void goo() + [Fact] + public async Task TestPreprocessorElIf() { + await TestAsync( + """ + #if ANY + #el[||]if SOME + #endif + """, "#elif"); } -}", "checked"); - } - [Fact] - public async Task TestChecked_11() - { - await Test_KeywordAsync( -@"public class C -{ - /// - /// - /// - void goo() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorElIf2() { + await TestAsync( + """ + #if ANY + #elif S[||]OME + #endif + """, "#elif"); } -}", "checked"); - } - - [Fact] - public async Task TestRequired() - { - await Test_KeywordAsync(""" - public class C - { - re[||]quired int Field; - } - """, "required"); - } - - [Fact] - public async Task TestDefaultConstraint() - { - await Test_KeywordAsync(""" - public class Base - { - virtual void M(T? t) { } - } - public class C - { - override void M() where T : def[||]ault { } - } - """, expectedText: "defaultconstraint"); - } - - [Fact] - public async Task TestDefaultCase() - { - await Test_KeywordAsync(""" - public class C - { - void M(object o) - { - switch (o) - { - case 1: - goto def[||]ault; - default: - return; - } - } - } - """, expectedText: "defaultcase"); - } - - [Fact] - public async Task TestGotoDefault() - { - await Test_KeywordAsync(""" - public class C - { - void M(object o) - { - switch (o) - { - case 1: - goto default; - def[||]ault: - return; - } - } - } - """, expectedText: "defaultcase"); - } - - [Fact] - public async Task TestLineDefault() - { - await Test_KeywordAsync(""" - #line def[||]ault - """, expectedText: "defaultline"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] - public async Task TestNotnull_OnType() - { - await Test_KeywordAsync(""" - public class C where T : not[||]null - { - } - """, expectedText: "notnull"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] - public async Task TestNotnull_OnMethod() - { - await Test_KeywordAsync(""" - public class C - { - void M() where T : not[||]null - { - } - } - """, expectedText: "notnull"); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorPragmaWarning1() + { + await TestAsync( + """ + #pragma warning disable CS[||]0312 + """, "#pragma"); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] - public async Task TestNotnull_FieldName() - { - await TestAsync(""" - public class C - { - int not[||]null = 0; - } - """, expectedText: "C.notnull"); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorPragmaWarning2() + { + await TestAsync( + """ + #pragm[||]a warning disable CS0312 + """, "#pragma"); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] - public async Task TestUnmanaged_OnType() - { - await Test_KeywordAsync(""" - public class C where T : un[||]managed - { - } - """, expectedText: "unmanaged"); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorPragmaWarning3() + { + await TestAsync( + """ + #pragma warni[||]ng disable CS0312 + """, "#warning"); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] - public async Task TestUnmanaged_OnMethod() - { - await Test_KeywordAsync(""" - public class C - { - void M() where T : un[||]managed - { - } - } - """, expectedText: "unmanaged"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65311")] - public async Task TestUnmanaged_LocalName() - { - await TestAsync(""" - int un[||]managed = 0; - """, expectedText: "System.Int32"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65312")] - public async Task TestSwitchStatement() - { - await Test_KeywordAsync(""" - swit[||]ch (1) { default: break; } - """, expectedText: "switch"); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65312")] - public async Task TestSwitchExpression() - { - await Test_KeywordAsync(""" - _ = 1 swit[||]ch { _ => 0 }; - """, expectedText: "switch-expression"); - } - - [Fact] - public async Task TestFile() - { - await Test_KeywordAsync(""" - fi[||]le class C { } - """, expectedText: "file"); - } - - [Fact] - public async Task TestRightShift() - { - await Test_KeywordAsync(""" - _ = 1 >[||]> 2; - """, expectedText: ">>"); - } - - [Fact] - public async Task TestUnsignedRightShift() - { - await Test_KeywordAsync(""" - _ = 1 >>[||]> 2; - """, expectedText: ">>>"); - } - - [Fact] - public async Task TestUnsignedRightShiftAssignment() - { - await Test_KeywordAsync(""" - 1 >>[||]>= 2; - """, expectedText: ">>>="); - } - - [Fact] - public async Task TestPreprocessorIf() - { - await TestAsync( -@" -#i[||]f ANY -#endif -", "#if"); - } - - [Fact] - public async Task TestPreprocessorIf2() - { - await TestAsync( -@" -#if ANY[||] -#endif -", "#if"); - } - - [Fact] - public async Task TestPreprocessorEndIf() - { - await TestAsync( -@" -#if ANY -#en[||]dif -", "#endif"); - } - - [Fact] - public async Task TestPreprocessorEndIf2() - { - await TestAsync( -@" -#if ANY -#endif[||] -", "#endif"); - } - - [Fact] - public async Task TestPreprocessorElse() - { - await TestAsync( -@" -#if ANY -#el[||]se -#endif -", "#else"); - } - - [Fact] - public async Task TestPreprocessorElse2() - { - await TestAsync( -@" -#if ANY -#else[||] -#endif -", "#else"); - } - - [Fact] - public async Task TestPreprocessorElIf() - { - await TestAsync( -@" -#if ANY -#el[||]if SOME -#endif -", "#elif"); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/66009")] + public async Task TestPreprocessorPragmaWarning4() + { + await TestAsync( + """ + #pragma warning dis[||]able CS0312 + """, "#disable"); } } diff --git a/src/VisualStudio/CSharp/Test/ProjectSystemShim/EntryPointFinderTests.cs b/src/VisualStudio/CSharp/Test/ProjectSystemShim/EntryPointFinderTests.cs new file mode 100644 index 0000000000000..c316cf267bc5e --- /dev/null +++ b/src/VisualStudio/CSharp/Test/ProjectSystemShim/EntryPointFinderTests.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Roslyn.VisualStudio.CSharp.UnitTests.ProjectSystemShim; + +[WorkItem("https://github.com/dotnet/roslyn/issues/35376")] +public sealed class EntryPointFinderTests +{ + [Theory, CombinatorialData] + public void PositiveTests( + [CombinatorialValues("public", "private", "")] string accessibility, + [CombinatorialValues("void", "int", "System.Int32", "Int32", "ValueTask", "Task", "ValueTask", "Task")] string returnType, + [CombinatorialValues("string[] args", "string[] args1", "")] string parameters) + { + Validate($"static {accessibility} {returnType} Main({parameters})", entryPoints => + { + Assert.Single(entryPoints); + Assert.Equal("C", entryPoints.Single().Name); + }); + } + + private static void NegativeTest(string signature) + => Validate(signature, Assert.Empty); + + private static void Validate(string signature, Action> validate) + { + var compilation = CSharpCompilation.Create("Test", references: [TestBase.MscorlibRef]).AddSyntaxTrees(CSharpSyntaxTree.ParseText($$""" + using System; + using System.Threading.Tasks; + + class C + { + {{signature}} + { + } + } + """)); + + var entryPoints = CSharpEntryPointFinder.FindEntryPoints(compilation); + validate(entryPoints); + } + + [Theory, CombinatorialData] + public void TestWrongName( + [CombinatorialValues("public", "private", "")] string accessibility, + [CombinatorialValues("void", "int", "System.Int32", "Int32", "ValueTask", "Task", "ValueTask", "Task")] string returnType, + [CombinatorialValues("string[] args", "string[] args1", "")] string parameters) + { + NegativeTest($"static {accessibility} {returnType} main({parameters})"); + } + + [Theory, CombinatorialData] + public void TestNotStatic( + [CombinatorialValues("public", "private", "")] string accessibility, + [CombinatorialValues("void", "int", "System.Int32", "Int32", "ValueTask", "Task", "ValueTask", "Task")] string returnType, + [CombinatorialValues("string[] args", "string[] args1", "")] string parameters) + { + NegativeTest($"{accessibility} {returnType} Main({parameters})"); + } + + [Theory, CombinatorialData] + public void TestInvalidReturnType( + [CombinatorialValues("public", "private", "")] string accessibility, + [CombinatorialValues("string", "Task", "ValueTask")] string returnType, + [CombinatorialValues("string[] args", "string[] args1", "")] string parameters) + { + NegativeTest($"static {accessibility} {returnType} Main({parameters})"); + } + + [Theory, CombinatorialData] + public void TestInvalidParameterTypes( + [CombinatorialValues("public", "private", "")] string accessibility, + [CombinatorialValues("void", "int", "System.Int32", "Int32", "ValueTask", "Task", "ValueTask", "Task")] string returnType, + [CombinatorialValues("string args", "string* args", "int[] args", "string[] args1, string[] args2")] string parameters) + { + NegativeTest($"static {accessibility} {returnType} Main({parameters})"); + } +} diff --git a/src/VisualStudio/Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs b/src/VisualStudio/Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs index 407b0d6dffce1..099c274c0f812 100644 --- a/src/VisualStudio/Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs +++ b/src/VisualStudio/Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs @@ -9,12 +9,10 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Progress; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; @@ -34,21 +32,14 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeCleanup; /// be implementing the interface, this abstract base class allows Roslyn to operate /// on MEF instances of fixers known to be relevant in the context of Roslyn languages. /// -internal abstract partial class AbstractCodeCleanUpFixer : ICodeCleanUpFixer +internal abstract partial class AbstractCodeCleanUpFixer( + IThreadingContext threadingContext, + VisualStudioWorkspaceImpl workspace, + IVsHierarchyItemManager vsHierarchyItemManager) : ICodeCleanUpFixer { - private readonly IThreadingContext _threadingContext; - private readonly VisualStudioWorkspaceImpl _workspace; - private readonly IVsHierarchyItemManager _vsHierarchyItemManager; - - protected AbstractCodeCleanUpFixer( - IThreadingContext threadingContext, - VisualStudioWorkspaceImpl workspace, - IVsHierarchyItemManager vsHierarchyItemManager) - { - _threadingContext = threadingContext; - _workspace = workspace; - _vsHierarchyItemManager = vsHierarchyItemManager; - } + private readonly IThreadingContext _threadingContext = threadingContext; + private readonly VisualStudioWorkspaceImpl _workspace = workspace; + private readonly IVsHierarchyItemManager _vsHierarchyItemManager = vsHierarchyItemManager; public Task FixAsync(ICodeCleanUpScope scope, ICodeCleanUpExecutionContext context) => scope switch @@ -151,19 +142,17 @@ private Task FixTextBufferAsync(TextBufferCodeCleanUpScope textBufferScope // Let LSP handle code cleanup in the cloud scenario if (buffer.IsInLspEditorContext()) - { return SpecializedTasks.False; - } var document = buffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) - { return SpecializedTasks.False; - } var workspace = buffer.GetWorkspace(); - Contract.ThrowIfNull(workspace); - return FixAsync(workspace, ApplyFixAsync, context); + if (workspace is not VisualStudioWorkspace visualStudioWorkspace) + return SpecializedTasks.False; + + return FixAsync(visualStudioWorkspace, ApplyFixAsync, context); // Local function async Task ApplyFixAsync(IProgress progress, CancellationToken cancellationToken) @@ -177,7 +166,7 @@ async Task ApplyFixAsync(IProgress progress, Can } private async Task FixAsync( - Workspace workspace, + VisualStudioWorkspace workspace, Func, CancellationToken, Task> applyFixAsync, ICodeCleanUpExecutionContext context) { diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractPackage.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractPackage.cs index ffabfa991b393..e6babc4236130 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractPackage.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractPackage.cs @@ -31,7 +31,7 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - _componentModel_doNotAccessDirectly = (IComponentModel)await GetServiceAsync(typeof(SComponentModel)).ConfigureAwait(true); + _componentModel_doNotAccessDirectly = (IComponentModel?)await GetServiceAsync(typeof(SComponentModel)).ConfigureAwait(true); Assumes.Present(_componentModel_doNotAccessDirectly); } diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index c73758af0f661..96995ad0c8d53 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -225,6 +225,7 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"csharp_style_prefer_switch_expression", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferSwitchExpression")}, {"csharp_style_prefer_top_level_statements", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferTopLevelStatements")}, {"csharp_style_prefer_tuple_swap", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferTupleSwap")}, + {"csharp_style_prefer_unbound_generic_type_in_nameof", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferUnboundGenericTypeInNameOf")}, {"csharp_style_prefer_utf8_string_literals", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferUtf8StringLiterals")}, {"csharp_style_throw_expression", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferThrowExpression")}, {"csharp_style_unused_value_assignment_preference", new RoamingProfileStorage("TextEditor.CSharp.Specific.UnusedValueAssignmentPreference")}, diff --git a/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs b/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs index 9a51a3f72fa62..70cb966833785 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphNavigatorExtension.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Linq; using System.Threading; @@ -32,74 +30,72 @@ internal sealed class GraphNavigatorExtension( public void NavigateTo(GraphObject graphObject) { + if (graphObject is not GraphNode graphNode) + return; - if (graphObject is GraphNode graphNode) - { - var sourceLocation = graphNode.GetValue(CodeNodeProperties.SourceLocation); - if (sourceLocation.FileName == null) - { - return; - } + _threadingContext.JoinableTaskFactory.Run(() => NavigateToAsync(graphNode, CancellationToken.None)); + } - var projectId = graphNode.GetValue(RoslynGraphProperties.ContextProjectId); - var symbolId = graphNode.GetValue(RoslynGraphProperties.SymbolId); + private async Task NavigateToAsync(GraphNode graphNode, CancellationToken cancellationToken) + { + var projectId = graphNode.GetValue(RoslynGraphProperties.ContextProjectId); - if (projectId != null) - { - var solution = _workspace.CurrentSolution; - var project = solution.GetProject(projectId); - if (project == null) - return; + if (projectId is null) + return; - var document = project.Documents.FirstOrDefault( - d => string.Equals( - d.FilePath, - sourceLocation.FileName.LocalPath, - StringComparison.OrdinalIgnoreCase)); + var solution = _workspace.CurrentSolution; + var project = solution.GetProject(projectId); + if (project is null) + return; - if (document == null) + // Go through the mainline symbol id path if we have it. That way we notify third parties, and we can navigate + // to metadata. + var symbolId = graphNode.GetValue(RoslynGraphProperties.SymbolId); + if (symbolId is not null) + { + var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); + if (compilation is not null) + { + var symbol = symbolId.Value.Resolve(compilation, cancellationToken: cancellationToken).GetAnySymbol(); + if (symbol is not null) + { + await GoToDefinitionHelpers.TryNavigateToLocationAsync( + symbol, project.Solution, _threadingContext, _streamingPresenter.Value, cancellationToken).ConfigureAwait(false); return; - - _threadingContext.JoinableTaskFactory.Run(() => - NavigateToAsync(sourceLocation, symbolId, project, document, CancellationToken.None)); + } } } - } - private async Task NavigateToAsync( - SourceLocation sourceLocation, SymbolKey? symbolId, Project project, Document document, CancellationToken cancellationToken) - { - // Notify of navigation so third parties can intercept the navigation - if (symbolId != null) - { - var symbol = symbolId.Value.Resolve(await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false), cancellationToken: cancellationToken).Symbol; - await GoToDefinitionHelpers.TryNavigateToLocationAsync( - symbol, project.Solution, _threadingContext, _streamingPresenter.Value, cancellationToken).ConfigureAwait(false); + // If we didn't have a symbol id, attempt to navigate to the source location directly if the node includes one. + var sourceLocation = graphNode.GetValue(CodeNodeProperties.SourceLocation); + if (sourceLocation.FileName is null || !sourceLocation.IsValid) return; - } - if (sourceLocation.IsValid) - { - // We must find the right document in this project. This may not be the - // ContextDocumentId if you have a partial member that is shown under one - // document, but only exists in the other + var document = project.Documents.FirstOrDefault( + d => string.Equals( + d.FilePath, + sourceLocation.FileName.LocalPath, + StringComparison.OrdinalIgnoreCase)); - if (document != null) - { - var editorWorkspace = document.Project.Solution.Workspace; - var navigationService = editorWorkspace.Services.GetService(); - - // TODO: Get the platform to use and pass us an operation context, or create one ourselves. - await navigationService.TryNavigateToLineAndOffsetAsync( - _threadingContext, - editorWorkspace, - document.Id, - sourceLocation.StartPosition.Line, - sourceLocation.StartPosition.Character, - NavigationOptions.Default, - cancellationToken).ConfigureAwait(false); - } - } + if (document == null) + return; + + // We must find the right document in this project. This may not be the + // ContextDocumentId if you have a partial member that is shown under one + // document, but only exists in the other + + var editorWorkspace = document.Project.Solution.Workspace; + var navigationService = editorWorkspace.Services.GetRequiredService(); + + // TODO: Get the platform to use and pass us an operation context, or create one ourselves. + await navigationService.TryNavigateToLineAndOffsetAsync( + _threadingContext, + editorWorkspace, + document.Id, + sourceLocation.StartPosition.Line, + sourceLocation.StartPosition.Character, + NavigationOptions.Default, + cancellationToken).ConfigureAwait(false); } public int GetRank(GraphObject graphObject) diff --git a/src/VisualStudio/Core/Def/ProjectSystem/AbstractEntryPointFinder.cs b/src/VisualStudio/Core/Def/ProjectSystem/AbstractEntryPointFinder.cs index 55b88934be5c0..4d909eb85fba3 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/AbstractEntryPointFinder.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/AbstractEntryPointFinder.cs @@ -2,75 +2,59 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Generic; -using System.Linq; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; -internal abstract class AbstractEntryPointFinder : SymbolVisitor +internal abstract class AbstractEntryPointFinder(Compilation compilation) : SymbolVisitor { protected readonly HashSet EntryPoints = []; + private readonly KnownTaskTypes _knownTaskTypes = new(compilation); + + protected abstract bool MatchesMainMethodName(string name); + public override void VisitNamespace(INamespaceSymbol symbol) { foreach (var member in symbol.GetMembers()) - { member.Accept(this); - } } public override void VisitNamedType(INamedTypeSymbol symbol) { foreach (var member in symbol.GetMembers()) - { member.Accept(this); - } } public override void VisitMethod(IMethodSymbol symbol) { - // named Main - if (!MatchesMainMethodName(symbol.Name)) - { - return; - } - - // static - if (!symbol.IsStatic) + // Similar to the form `static void Main(string[] args)` (and varying permutations). + if (symbol.IsStatic && + MatchesMainMethodName(symbol.Name) && + HasValidReturnType(symbol) && + symbol.Parameters is [{ Type: IArrayTypeSymbol { ElementType.SpecialType: SpecialType.System_String } }] or []) { - return; + EntryPoints.Add(symbol.ContainingType); } + } - // returns void or int - if (!symbol.ReturnsVoid && symbol.ReturnType.SpecialType != SpecialType.System_Int32) - { - return; - } + private bool HasValidReturnType(IMethodSymbol symbol) + { + // void + if (symbol.ReturnsVoid) + return true; - // parameterless or takes a string[] - if (symbol.Parameters.Length == 1) - { - var parameter = symbol.Parameters.Single(); - if (parameter.Type is IArrayTypeSymbol) - { - var elementType = ((IArrayTypeSymbol)parameter.Type).ElementType; - var specialType = elementType.SpecialType; + var returnType = symbol.ReturnType; - if (specialType == SpecialType.System_String) - { - EntryPoints.Add(symbol.ContainingType); - } - } - } + // int + if (returnType.SpecialType == SpecialType.System_Int32) + return true; - if (!symbol.Parameters.Any()) - { - EntryPoints.Add(symbol.ContainingType); - } + // Task or ValueTask + // Task or ValueTask + return _knownTaskTypes.IsTaskLike(returnType) && + returnType.GetTypeArguments() is [] or [{ SpecialType: SpecialType.System_Int32 }]; } - - protected abstract bool MatchesMainMethodName(string name); } diff --git a/src/VisualStudio/Core/Def/ProjectSystem/AbstractEntryPointFinderService.cs b/src/VisualStudio/Core/Def/ProjectSystem/AbstractEntryPointFinderService.cs new file mode 100644 index 0000000000000..cf28888a703c5 --- /dev/null +++ b/src/VisualStudio/Core/Def/ProjectSystem/AbstractEntryPointFinderService.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Microsoft.CodeAnalysis; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; + +internal abstract class AbstractEntryPointFinderService : IEntryPointFinderService +{ + protected abstract IEnumerable FindEntryPoints(Compilation compilation, bool findFormsOnly); + + public IEnumerable FindEntryPoints(INamespaceSymbol symbol, bool findFormsOnly) + => symbol is not { ContainingAssembly: ISourceAssemblySymbol sourceAssembly } + ? [] + : FindEntryPoints(sourceAssembly.Compilation, findFormsOnly); +} diff --git a/src/VisualStudio/Core/Def/ProjectSystem/FileChangeWatcher.cs b/src/VisualStudio/Core/Def/ProjectSystem/FileChangeWatcher.cs index 41701d2faf735..8d3e56b3365fb 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/FileChangeWatcher.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/FileChangeWatcher.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.ProjectSystem; @@ -44,10 +45,10 @@ public FileChangeWatcher( { _fileChangeService = fileChangeService; - // 📝 Empirical testing during high activity (e.g. solution close) showed strong batching performance even - // though the batching delay is 0. + // 📝 Empirical testing during high activity (e.g. solution open/close) showed strong batching performance + // with delay of 500 ms, batching over 95% of the calls. _taskQueue = new AsyncBatchingWorkQueue( - TimeSpan.Zero, + TimeSpan.FromMilliseconds(500), ProcessBatchAsync, listenerProvider.GetListener(FeatureAttribute.Workspace), CancellationToken.None); @@ -98,10 +99,10 @@ private readonly struct WatcherOperation private readonly Kind _kind; /// - /// The extension filter to apply for . This value may be - /// to disable the extension filter. + /// The extension filters to apply for . This value may be + /// empty to disable the extension filter. /// - private readonly string? _filter; + private readonly ImmutableArray _filters; /// /// The file change flags to apply for . @@ -139,13 +140,13 @@ private readonly struct WatcherOperation /// private readonly OneOrMany _paths; - private WatcherOperation(Kind kind, string directory, string? filter, IVsFreeThreadedFileChangeEvents2 sink, List cookies) + private WatcherOperation(Kind kind, string directory, ImmutableArray filters, IVsFreeThreadedFileChangeEvents2 sink, List cookies) { Contract.ThrowIfFalse(kind is Kind.WatchDirectory); _kind = kind; _paths = new OneOrMany(directory); - _filter = filter; + _filters = filters.NullToEmpty(); _sink = sink; _cookies = cookies; @@ -165,7 +166,7 @@ private WatcherOperation(Kind kind, OneOrMany files, _VSFILECHANGEFLAGS _tokens = tokens; // Other watching fields are not used for this kind - _filter = null; + _filters = ImmutableArray.Empty; _cookies = null!; } @@ -177,7 +178,7 @@ private WatcherOperation(Kind kind, List cookies) _cookies = cookies; // Other watching fields are not used for this kind - _filter = null; + _filters = ImmutableArray.Empty; _fileChangeFlags = 0; _sink = null!; _tokens = OneOrMany.Empty; @@ -192,7 +193,7 @@ private WatcherOperation(Kind kind, OneOrMany tokens _tokens = tokens; // Other watching fields are not used for this kind - _filter = null; + _filters = ImmutableArray.Empty; _fileChangeFlags = 0; _sink = null!; _cookies = null!; @@ -207,8 +208,8 @@ private enum Kind UnwatchFiles, } - public static WatcherOperation WatchDirectory(string directory, string? filter, IVsFreeThreadedFileChangeEvents2 sink, List cookies) - => new(Kind.WatchDirectory, directory, filter, sink, cookies); + public static WatcherOperation WatchDirectory(string directory, ImmutableArray filters, IVsFreeThreadedFileChangeEvents2 sink, List cookies) + => new(Kind.WatchDirectory, directory, filters, sink, cookies); public static WatcherOperation WatchFile(string path, _VSFILECHANGEFLAGS fileChangeFlags, IVsFreeThreadedFileChangeEvents2 sink, Context.RegularWatchedFile token) => new(Kind.WatchFiles, OneOrMany.Create(path), fileChangeFlags, sink, OneOrMany.Create(token)); @@ -252,21 +253,15 @@ public static WatcherOperation CombineRange(ImmutableSegmentedList 0) + await service.FilterDirectoryChangesAsync(cookie, _filters.ToArray(), cancellationToken).ConfigureAwait(false); return; @@ -367,8 +363,13 @@ public Context(FileChangeWatcher fileChangeWatcher, ImmutableArray WatcherOperation.WatchDirectory(watchedDirectory.Path, watchedDirectory.ExtensionFilter, this, _directoryWatchCookies))); + var item = WatcherOperation.WatchDirectory( + watchedDirectory.Path, + watchedDirectory.ExtensionFilters, + this, + _directoryWatchCookies); + + _fileChangeWatcher._taskQueue.AddWork(item); } } diff --git a/src/VisualStudio/Core/Def/ProjectSystem/IEntryPointFinderService.cs b/src/VisualStudio/Core/Def/ProjectSystem/IEntryPointFinderService.cs index 005559ed71323..56b0abe12a737 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/IEntryPointFinderService.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/IEntryPointFinderService.cs @@ -2,14 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Generic; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Host; namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; +/// +/// Note: This type is exposed through IVT to dotnet-project-system. +/// internal interface IEntryPointFinderService : ILanguageService { /// diff --git a/src/VisualStudio/Core/Def/RoslynPackage.cs b/src/VisualStudio/Core/Def/RoslynPackage.cs index 1ec2bec5601c6..d0980fe34b761 100644 --- a/src/VisualStudio/Core/Def/RoslynPackage.cs +++ b/src/VisualStudio/Core/Def/RoslynPackage.cs @@ -276,7 +276,8 @@ private async Task LoadStackTraceExplorerMenusAsync(CancellationToken cancellati // Obtain services and QueryInterface from the main thread await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - var menuCommandService = (OleMenuCommandService)await GetServiceAsync(typeof(IMenuCommandService)).ConfigureAwait(true); + var menuCommandService = (OleMenuCommandService?)await GetServiceAsync(typeof(IMenuCommandService)).ConfigureAwait(true); + Assumes.Present(menuCommandService); StackTraceExplorerCommandHandler.Initialize(menuCommandService, this); } diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.resx b/src/VisualStudio/Core/Def/ServicesVSResources.resx index b0217c12d0415..0ecb04dcae98a 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.resx +++ b/src/VisualStudio/Core/Def/ServicesVSResources.resx @@ -1937,6 +1937,9 @@ Additional information: {1} Prefer static anonymous functions + + Prefer unbound generic type in 'nameof' + Show hints for collection expressions diff --git a/src/VisualStudio/Core/Def/Workspace/VisualStudioDocumentNavigationService.cs b/src/VisualStudio/Core/Def/Workspace/VisualStudioDocumentNavigationService.cs index 5d1a9ca5d5377..97ec5a757a3ab 100644 --- a/src/VisualStudio/Core/Def/Workspace/VisualStudioDocumentNavigationService.cs +++ b/src/VisualStudio/Core/Def/Workspace/VisualStudioDocumentNavigationService.cs @@ -228,10 +228,12 @@ await getTextSpanForMappingAsync(document).ConfigureAwait(false), // If the mapped file maps to the same document that was passed in, then re-use the documentId to preserve context. // Otherwise, just pick one of the ids to use for navigation. var documentIdToNavigate = documentIdsForFilePath.Contains(documentId) ? documentId : documentIdsForFilePath.First(); - return GetNavigationCallback( - documentIdToNavigate, - workspace, - sourceText => getVsTextSpanForMapping(sourceText, mappedSpan.Span)); + + // For Venus documents, further mapping is done in the callback, so we don't want to do it here via getVsTextSpanForMapping + var getSpanForCallback = IsSecondaryBuffer(documentIdToNavigate) + ? getVsTextSpan + : sourceText => getVsTextSpanForMapping(sourceText, mappedSpan.Span); + return GetNavigationCallback(documentIdToNavigate, workspace, getSpanForCallback); } return await GetNavigableLocationForMappedFileAsync( diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf index ab6a97642382e..14ec395e6aa25 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf @@ -1137,6 +1137,11 @@ Preferovat prohození řazené kolekce členů + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Projekty diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf index 4661f9d499406..5e094a022f2aa 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf @@ -1137,6 +1137,11 @@ Tupeltausch bevorzugen + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Projekte diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf index 050c1396a632b..3fd9228e754aa 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf @@ -1137,6 +1137,11 @@ Preferir el intercambio de tupla + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Proyectos diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf index 4e747c8f91677..e30845d56ee56 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf @@ -1137,6 +1137,11 @@ Préférer l'échange de tuples + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Projets diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf index fed1715b3b0e6..3484693536d83 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf @@ -1137,6 +1137,11 @@ Preferisci lo scambio di tuple + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Progetti diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf index 0a4741c59619b..0858bfbcc0615 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf @@ -1137,6 +1137,11 @@ タプル スワップを優先する + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects プロジェクト diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf index 81d5aa5c4d644..7a3b22ba4c79e 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf @@ -1137,6 +1137,11 @@ 튜플 교환 선호 + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects 프로젝트 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf index 1c458bf2c9478..4e05e910c707c 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf @@ -1137,6 +1137,11 @@ Preferuj zamianę krotki + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Projekty diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf index 896e88a2cf757..ae8395e136779 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf @@ -1137,6 +1137,11 @@ Prefiro troca de tupla + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Projetos diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf index c2a0f7fb1a35a..3f797ccbfc7a7 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf @@ -1137,6 +1137,11 @@ Предпочитать переключение кортежей + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Проекты diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf index 1562cb143bf76..4199fefd315d5 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf @@ -1137,6 +1137,11 @@ Demet değiştirmeyi tercih et + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects Projeler diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf index 6c5841751c0ec..072fcff0111bd 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf @@ -1137,6 +1137,11 @@ 首选元组交换 + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects 项目 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf index 600e7685c0a2e..e2928546213c9 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf @@ -1137,6 +1137,11 @@ 偏好元組交換 + + Prefer unbound generic type in 'nameof' + Prefer unbound generic type in 'nameof' + + Projects 專案 diff --git a/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs b/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs index 664c25c3a3306..95062848499db 100644 --- a/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs +++ b/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs @@ -34,13 +34,9 @@ private AssemblyLoadTestFixtureCollection() { } [Collection(AssemblyLoadTestFixtureCollection.Name)] [UseExportProvider] - public class SnapshotSerializationTests + public sealed class SnapshotSerializationTests(AssemblyLoadTestFixture testFixture) { - private readonly AssemblyLoadTestFixture _testFixture; - public SnapshotSerializationTests(AssemblyLoadTestFixture testFixture) - { - _testFixture = testFixture; - } + private readonly AssemblyLoadTestFixture _testFixture = testFixture; private static Workspace CreateWorkspace(Type[] additionalParts = null) => new AdhocWorkspace(FeaturesTestCompositions.Features.AddParts(additionalParts).WithTestHostParts(TestHost.OutOfProcess).GetHostServices()); @@ -58,19 +54,17 @@ internal static Solution SetFullSolution(Workspace workspace) var document2 = project2.AddDocument("Document2", SourceText.From(vbCode)); solution = document2.Project.Solution.GetRequiredProject(project1.Id) - .AddProjectReference(new ProjectReference(project2.Id, ImmutableArray.Create("test"))) + .AddProjectReference(new ProjectReference(project2.Id, ["test"])) .AddMetadataReference(MetadataReference.CreateFromFile(typeof(object).Assembly.Location)) .AddAnalyzerReference(new AnalyzerFileReference(Path.Combine(TempRoot.Root, "path1"), new TestAnalyzerAssemblyLoader())) - .AddAdditionalDocument("Additional", SourceText.From("hello"), ImmutableArray.Create("test"), @".\Add").Project.Solution; + .AddAdditionalDocument("Additional", SourceText.From("hello"), ["test"], @".\Add").Project.Solution; return solution .WithAnalyzerReferences([new AnalyzerFileReference(Path.Combine(TempRoot.Root, "path2"), new TestAnalyzerAssemblyLoader())]) - .AddAnalyzerConfigDocuments( - ImmutableArray.Create( - DocumentInfo.Create( - DocumentId.CreateNewId(project1.Id), - ".editorconfig", - loader: TextLoader.From(TextAndVersion.Create(SourceText.From("root = true"), VersionStamp.Create()))))); + .AddAnalyzerConfigDocuments([DocumentInfo.Create( + DocumentId.CreateNewId(project1.Id), + ".editorconfig", + loader: TextLoader.From(TextAndVersion.Create(SourceText.From("root = true"), VersionStamp.Create())))]); }, WorkspaceChangeKind.SolutionChanged); return workspace.CurrentSolution; diff --git a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs index 512a6ab9edd6c..fd9622f9ae376 100644 --- a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs @@ -89,15 +89,15 @@ public async Task TestRemoteHostTextSynchronize() // update text var newText = oldText.WithChanges(new TextChange(TextSpan.FromBounds(0, 0), "/* test */")); - // sync - await client.TryInvokeAsync( - (service, cancellationToken) => service.SynchronizeTextAsync(oldDocument.Id, oldState.Text, newText.GetTextChanges(oldText).AsImmutable(), cancellationToken), - CancellationToken.None); - // apply change to solution var newDocument = oldDocument.WithText(newText); var newState = await newDocument.State.GetStateChecksumsAsync(CancellationToken.None); + // sync + await client.TryInvokeAsync( + (service, cancellationToken) => service.SynchronizeTextChangesAsync([(oldDocument.Id, oldState.Text, newText.GetTextChanges(oldText).AsImmutable(), newState.Text)], cancellationToken), + CancellationToken.None); + // check that text already exist in remote side Assert.True(client.TestData.WorkspaceManager.SolutionAssetCache.TryGetAsset(newState.Text, out var serializableRemoteText)); Assert.Equal(newText.ToString(), (await serializableRemoteText.GetTextAsync(CancellationToken.None)).ToString()); diff --git a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs index 7acae046c03eb..e74b1d5764919 100644 --- a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs @@ -1048,6 +1048,48 @@ class Program2 objectReference2.AssertReleased(); } + [Fact] + public void TestActiveAndRelatedDocumentSemanticModelCached() + { + using var workspace = TestWorkspace.Create(""" + + + + class Program1 + { + } + + + class Program2 + { + } + + + + + + + """, composition: s_compositionWithFirstDocumentIsActiveAndVisible); + using var remoteWorkspace = CreateRemoteWorkspace(); + + var solution = workspace.CurrentSolution; + + var project1 = solution.Projects.Single(p => p.AssemblyName == "Assembly1"); + var project2 = solution.Projects.Single(p => p.AssemblyName == "Assembly2"); + var document1 = project1.Documents.First(); + var document2 = project1.Documents.Last(); + var document3 = project2.Documents.Single(); + + // Only the semantic model for the active document should be cached. + var objectReference1 = ObjectReference.CreateFromFactory(() => document1.GetSemanticModelAsync().GetAwaiter().GetResult()); + var objectReference2 = ObjectReference.CreateFromFactory(() => document2.GetSemanticModelAsync().GetAwaiter().GetResult()); + var objectReference3 = ObjectReference.CreateFromFactory(() => document3.GetSemanticModelAsync().GetAwaiter().GetResult()); + + objectReference1.AssertHeld(); + objectReference2.AssertReleased(); + objectReference3.AssertHeld(); + } + [Fact] public async Task TestRemoteWorkspaceCachesNothingIfActiveDocumentNotSynced() { diff --git a/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb b/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb index e3be0cb90efd8..d04445a96d69a 100644 --- a/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb +++ b/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb @@ -158,6 +158,7 @@ csharp_style_prefer_local_over_anonymous_function = true csharp_style_prefer_null_check_over_type_check = true csharp_style_prefer_range_operator = true csharp_style_prefer_tuple_swap = true +csharp_style_prefer_unbound_generic_type_in_nameof = true csharp_style_prefer_utf8_string_literals = true csharp_style_throw_expression = true csharp_style_unused_value_assignment_preference = discard_variable @@ -417,6 +418,7 @@ csharp_style_prefer_local_over_anonymous_function = true csharp_style_prefer_null_check_over_type_check = true csharp_style_prefer_range_operator = true csharp_style_prefer_tuple_swap = true +csharp_style_prefer_unbound_generic_type_in_nameof = true csharp_style_prefer_utf8_string_literals = true csharp_style_throw_expression = true csharp_style_unused_value_assignment_preference = discard_variable diff --git a/src/VisualStudio/Core/Test/ProjectSystemShim/FileChangeWatcherTests.vb b/src/VisualStudio/Core/Test/ProjectSystemShim/FileChangeWatcherTests.vb new file mode 100644 index 0000000000000..ce58e9645d014 --- /dev/null +++ b/src/VisualStudio/Core/Test/ProjectSystemShim/FileChangeWatcherTests.vb @@ -0,0 +1,57 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Collections.Immutable +Imports System.IO +Imports Microsoft.CodeAnalysis.ProjectSystem +Imports Microsoft.CodeAnalysis.Shared.TestHooks +Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem +Imports Roslyn.Test.Utilities +Imports IVsAsyncFileChangeEx2 = Microsoft.VisualStudio.Shell.IVsAsyncFileChangeEx2 + +Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim + + Public NotInheritable Class FileChangeWatcherTests + Implements IDisposable + + Private ReadOnly _tempPath As String + + Public Sub New() + _tempPath = Path.Combine(TempRoot.Root, Path.GetRandomFileName()) + Directory.CreateDirectory(_tempPath) + End Sub + + Private Sub Dispose() Implements IDisposable.Dispose + Directory.Delete(_tempPath, recursive:=True) + End Sub + + + Public Async Function WatchingMultipleContexts() As Task + Using workspace = New EditorTestWorkspace() + Dim fileChangeService = New MockVsFileChangeEx + Dim fileChangeWatcher = New FileChangeWatcher(workspace.GetService(Of IAsynchronousOperationListenerProvider)(), Task.FromResult(Of IVsAsyncFileChangeEx2)(fileChangeService)) + + Dim context1 = fileChangeWatcher.CreateContext(ImmutableArray.Create(New WatchedDirectory(_tempPath, ImmutableArray(Of String).Empty))) + Dim context2 = fileChangeWatcher.CreateContext(ImmutableArray.Create(New WatchedDirectory(_tempPath, ImmutableArray(Of String).Empty))) + + Dim handler1Called As Boolean = False + Dim handler2Called As Boolean = False + + AddHandler context1.FileChanged, Sub(sender, args) handler1Called = True + AddHandler context2.FileChanged, Sub(sender, args) handler2Called = True + + Dim watchedFile1 = context1.EnqueueWatchingFile("file1.txt") + Dim watchedFile2 = context2.EnqueueWatchingFile("file2.txt") + + Await workspace.GetService(Of AsynchronousOperationListenerProvider)().GetWaiter(FeatureAttribute.Workspace).ExpeditedWaitAsync() + + fileChangeService.FireUpdate("file2.txt") + + Assert.False(handler1Called) + Assert.True(handler2Called) + End Using + End Function + End Class +End Namespace diff --git a/src/VisualStudio/DevKit/Impl/EditAndContinue/ManagedHotReloadLanguageServiceBridge.cs b/src/VisualStudio/DevKit/Impl/EditAndContinue/ManagedHotReloadLanguageServiceBridge.cs index e9cb2154246b9..21f848e25a5ab 100644 --- a/src/VisualStudio/DevKit/Impl/EditAndContinue/ManagedHotReloadLanguageServiceBridge.cs +++ b/src/VisualStudio/DevKit/Impl/EditAndContinue/ManagedHotReloadLanguageServiceBridge.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Immutable; using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; @@ -17,7 +18,7 @@ namespace Microsoft.CodeAnalysis.EditAndContinue; [ExportBrokeredService(ManagedHotReloadLanguageServiceDescriptor.MonikerName, ManagedHotReloadLanguageServiceDescriptor.ServiceVersion, Audience = ServiceAudience.Local)] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed partial class ManagedHotReloadLanguageServiceBridge(InternalContracts.IManagedHotReloadLanguageService service) : IManagedHotReloadLanguageService, IExportedBrokeredService +internal sealed partial class ManagedHotReloadLanguageServiceBridge(InternalContracts.IManagedHotReloadLanguageService2 service) : IManagedHotReloadLanguageService2, IExportedBrokeredService { ServiceRpcDescriptor IExportedBrokeredService.Descriptor => ManagedHotReloadLanguageServiceDescriptor.Descriptor; @@ -43,9 +44,15 @@ public ValueTask OnCapabilitiesChangedAsync(CancellationToken cancellationToken) public async ValueTask GetUpdatesAsync(CancellationToken cancellationToken) => (await service.GetUpdatesAsync(cancellationToken).ConfigureAwait(false)).FromContract(); + public async ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken) + => (await service.GetUpdatesAsync(runningProjects, cancellationToken).ConfigureAwait(false)).FromContract(); + public ValueTask CommitUpdatesAsync(CancellationToken cancellationToken) => service.CommitUpdatesAsync(cancellationToken); + public ValueTask UpdateBaselinesAsync(ImmutableArray projectPaths, CancellationToken cancellationToken) + => service.UpdateBaselinesAsync(projectPaths, cancellationToken); + public ValueTask DiscardUpdatesAsync(CancellationToken cancellationToken) => service.DiscardUpdatesAsync(cancellationToken); diff --git a/src/VisualStudio/ExternalAccess/FSharp/Internal/Navigation/FSharpFindDefinitionService.cs b/src/VisualStudio/ExternalAccess/FSharp/Internal/Navigation/FSharpFindDefinitionService.cs index 7c38d20db56b3..d95506b9b278f 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Internal/Navigation/FSharpFindDefinitionService.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Internal/Navigation/FSharpFindDefinitionService.cs @@ -23,4 +23,9 @@ public async Task> GetNavigableItemsAsync(Documen var items = await service.FindDefinitionsAsync(document, position, cancellationToken).ConfigureAwait(false); return items.SelectAsArray(x => (INavigableItem)new InternalFSharpNavigableItem(x)); } + + public Task> GetNavigableItemsAsync(Document document, int position, bool forSymbolType, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs index 08bd9fc336185..e7ea9428208d1 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpCodeActions.cs @@ -25,51 +25,46 @@ namespace Roslyn.VisualStudio.NewIntegrationTests.CSharp; -public class CSharpCodeActions : AbstractEditorTest +public sealed class CSharpCodeActions() : AbstractEditorTest(nameof(CSharpCodeActions)) { - public CSharpCodeActions() - : base(nameof(CSharpCodeActions)) - { - } - protected override string LanguageName => LanguageNames.CSharp; [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] public async Task GenerateMethodInClosedFile() { var project = ProjectName; - await TestServices.SolutionExplorer.AddFileAsync(project, "Foo.cs", contents: @" -public class Foo -{ -} -", cancellationToken: HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddFileAsync(project, "Foo.cs", contents: """ + public class Foo + { + } + """, cancellationToken: HangMitigatingCancellationToken); - await SetUpEditorAsync(@" -using System; + await SetUpEditorAsync(""" + using System; -public class Program -{ - public static void Main(string[] args) - { - Foo f = new Foo(); - f.Bar()$$ - } -} -", HangMitigatingCancellationToken); + public class Program + { + public static void Main(string[] args) + { + Foo f = new Foo(); + f.Bar()$$ + } + } + """, HangMitigatingCancellationToken); await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); await TestServices.EditorVerifier.CodeActionAsync("Generate method 'Bar'", applyFix: true, cancellationToken: HangMitigatingCancellationToken); - await TestServices.SolutionVerifier.FileContentsAsync(project, "Foo.cs", @" -using System; + await TestServices.SolutionVerifier.FileContentsAsync(project, "Foo.cs", """ + using System; -public class Foo -{ - internal void Bar() - { - throw new NotImplementedException(); - } -} -", HangMitigatingCancellationToken); + public class Foo + { + internal void Bar() + { + throw new NotImplementedException(); + } + } + """, HangMitigatingCancellationToken); } [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)] @@ -77,12 +72,12 @@ public async Task AddUsingOnIncompleteMember() { // Need to ensure that incomplete member diagnostics run at high pri so that add-using can be // triggered by them. - await SetUpEditorAsync(@" -class Program -{ - DateTime$$ -} -", HangMitigatingCancellationToken); + await SetUpEditorAsync(""" + class Program + { + DateTime$$ + } + """, HangMitigatingCancellationToken); await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); await TestServices.EditorVerifier.CodeActionAsync("using System;", cancellationToken: HangMitigatingCancellationToken); } @@ -95,15 +90,15 @@ public async Task FastDoubleInvoke() // to get it to invoke without any sort of waiting to happen. This helps address a bug // we had where our asynchronous smart tags interfered with asynchrony in VS, which caused // the second smart tag to not expand if you tried invoking it too quickly - await SetUpEditorAsync(@" -class Program -{ - static void Main(string[] args) - { - Exception $$ex = new System.ArgumentException(); - } -} -", HangMitigatingCancellationToken); + await SetUpEditorAsync(""" + class Program + { + static void Main(string[] args) + { + Exception $$ex = new System.ArgumentException(); + } + } + """, HangMitigatingCancellationToken); // Suspend file change notification during code action application, since spurious file change notifications // can cause silent failure to apply the code action if they occur within this block. @@ -122,44 +117,46 @@ static void Main(string[] args) } await TestServices.EditorVerifier.TextContainsAsync( - @" -using System; + """ + using System; -class Program -{ - static void Main(string[] args) - { - Exception ex = new ArgumentException(); - } -}", cancellationToken: HangMitigatingCancellationToken); + class Program + { + static void Main(string[] args) + { + Exception ex = new ArgumentException(); + } + } + """, cancellationToken: HangMitigatingCancellationToken); } [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsInvokeDelegateWithConditionalAccess)] public async Task InvokeDelegateWithConditionalAccessMultipleTimes() { - var markup = @" -using System; -class C -{ - public event EventHandler First; - public event EventHandler Second; - void RaiseFirst() - { - var temp1 = First; - if (temp1 != null) - { - temp1$$(this, EventArgs.Empty); - } - } - void RaiseSecond() - { - var temp2 = Second; - if (temp2 != null) - { - temp2(this, EventArgs.Empty); - } - } -}"; + var markup = """ + using System; + class C + { + public event EventHandler First; + public event EventHandler Second; + void RaiseFirst() + { + var temp1 = First; + if (temp1 != null) + { + temp1$$(this, EventArgs.Empty); + } + } + void RaiseSecond() + { + var temp2 = Second; + if (temp2 != null) + { + temp2(this, EventArgs.Empty); + } + } + } + """; MarkupTestFile.GetSpans(markup, out _, out var _); await SetUpEditorAsync(markup, HangMitigatingCancellationToken); @@ -179,40 +176,42 @@ void RaiseSecond() [WorkItem("https://github.com/dotnet/roslyn/issues/19089")] public async Task ApplyEditorConfigAndFixAllOccurrences() { - var markup = @" -class C -{ - public int X1 - { - get - { - $$return 3; - } - } - - public int Y1 => 5; - - public int X2 - { - get - { - return 3; - } - } - - public int Y2 => 5; -}"; - var expectedText = @" -class C -{ - public int X1 => 3; + var markup = """ + class C + { + public int X1 + { + get + { + $$return 3; + } + } + + public int Y1 => 5; + + public int X2 + { + get + { + return 3; + } + } + + public int Y2 => 5; + } + """; + var expectedText = """ + class C + { + public int X1 => 3; - public int Y1 => 5; + public int Y1 => 5; - public int X2 => 3; + public int X2 => 3; - public int Y2 => 5; -}"; + public int Y2 => 5; + } + """; await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken); @@ -234,11 +233,12 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( HangMitigatingCancellationToken); await TestServices.EditorVerifier.CodeActionsNotShowingAsync(HangMitigatingCancellationToken); - var editorConfig = @"root = true + var editorConfig = """ + root = true -[*.cs] -csharp_style_expression_bodied_properties = true:warning -"; + [*.cs] + csharp_style_expression_bodied_properties = true:warning + """; await TestServices.SolutionExplorer.AddFileAsync(ProjectName, ".editorconfig", editorConfig, open: false, HangMitigatingCancellationToken); @@ -283,41 +283,42 @@ await TestServices.EditorVerifier.CodeActionAsync( fixAllScope: FixAllScope.Project, cancellationToken: HangMitigatingCancellationToken); - expectedText = @" -class C -{ - public int X1 - { - get - { - return 3; - } - } - - public int Y1 - { - get - { - return 5; - } - } - - public int X2 - { - get - { - return 3; - } - } - - public int Y2 - { - get - { - return 5; - } - } -}"; + expectedText = """ + class C + { + public int X1 + { + get + { + return 3; + } + } + + public int Y1 + { + get + { + return 5; + } + } + + public int X2 + { + get + { + return 3; + } + } + + public int Y2 + { + get + { + return 5; + } + } + } + """; AssertEx.EqualOrDiff(expectedText, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); } @@ -327,38 +328,36 @@ public int Y2 [InlineData(FixAllScope.Solution)] [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] [WorkItem("https://github.com/dotnet/roslyn/issues/33507")] + [WorkItem("https://github.com/dotnet/roslyn/issues/74761")] public async Task FixAllOccurrencesIgnoresGeneratedCode(FixAllScope scope) { - var markup = @" -using System; -using $$System.Threading; + var markup = """ + using System; + using $$System.Threading; -class C -{ - public IntPtr X1 { get; set; } -}"; - var expectedText = @" -using System; - -class C -{ - public IntPtr X1 { get; set; } -}"; - var generatedSourceMarkup = @"// -using System; -using $$System.Threading; + class C + { + public IntPtr X1 { get; set; } + } + """; + var expectedText = """ + using System; -class D -{ - public IntPtr X1 { get; set; } -}"; - var expectedGeneratedSource = @"// -using System; + class C + { + public IntPtr X1 { get; set; } + } + """; + var generatedSourceMarkup = """ + // + using System; + using $$System.Threading; -class D -{ - public IntPtr X1 { get; set; } -}"; + class D + { + public IntPtr X1 { get; set; } + } + """; MarkupTestFile.GetPosition(generatedSourceMarkup, out var generatedSource, out int generatedSourcePosition); @@ -387,58 +386,34 @@ await TestServices.EditorVerifier.CodeActionAsync( // The current behavior is observable; any change to this behavior should be part of an intentional design // change. await TestServices.Editor.MoveCaretAsync(generatedSourcePosition, HangMitigatingCancellationToken); - await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); - await TestServices.EditorVerifier.CodeActionAsync( - "Remove unnecessary usings", - applyFix: true, - fixAllScope: FixAllScope.Document, - cancellationToken: HangMitigatingCancellationToken); - - AssertEx.EqualOrDiff(generatedSource, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); - - // Verify that the code action can still be applied manually from within the generated file. - // This is a regression test for correctness with respect to the design. - await TestServices.Editor.MoveCaretAsync(generatedSourcePosition, HangMitigatingCancellationToken); - await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); - await TestServices.EditorVerifier.CodeActionAsync( - "Remove unnecessary usings", - applyFix: true, - fixAllScope: null, - cancellationToken: HangMitigatingCancellationToken); - - AssertEx.EqualOrDiff(expectedGeneratedSource, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); + await TestServices.EditorVerifier.CodeActionsNotShowingAsync(HangMitigatingCancellationToken); } - [CriticalIdeTheory] - [InlineData(FixAllScope.Project)] - [InlineData(FixAllScope.Solution)] + [IdeFact] [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] [WorkItem("https://github.com/dotnet/roslyn/issues/33507")] - public async Task FixAllOccurrencesTriggeredFromGeneratedCode(FixAllScope scope) + [WorkItem("https://github.com/dotnet/roslyn/issues/74761")] + public async Task FixAllOccurrencesNotTriggeredFromGeneratedCode() { - var markup = @"// -using System; -using $$System.Threading; - -class C -{ - public IntPtr X1 { get; set; } -}"; - var secondFile = @" -using System; -using System.Threading; + var markup = """ + // + using System; + using $$System.Threading; -class D -{ - public IntPtr X1 { get; set; } -}"; - var expectedSecondFile = @" -using System; + class C + { + public IntPtr X1 { get; set; } + } + """; + var secondFile = """ + using System; + using System.Threading; -class D -{ - public IntPtr X1 { get; set; } -}"; + class D + { + public IntPtr X1 { get; set; } + } + """; await TestServices.SolutionExplorer.AddFileAsync(ProjectName, "D.cs", secondFile, open: false, HangMitigatingCancellationToken); @@ -451,30 +426,26 @@ class D // change. MarkupTestFile.GetPosition(markup, out var expectedText, out int _); await SetUpEditorAsync(markup, HangMitigatingCancellationToken); - await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); - await TestServices.EditorVerifier.CodeActionAsync( - "Remove unnecessary usings", - applyFix: true, - fixAllScope: scope, - cancellationToken: HangMitigatingCancellationToken); + await TestServices.EditorVerifier.CodeActionsNotShowingAsync(HangMitigatingCancellationToken); AssertEx.EqualOrDiff(expectedText, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "D.cs", HangMitigatingCancellationToken); - AssertEx.EqualOrDiff(expectedSecondFile, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); + AssertEx.EqualOrDiff(secondFile, await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken)); } [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] public async Task ClassificationInPreviewPane() { - await SetUpEditorAsync(@" -class Program -{ - int Main() - { - Foo$$(); - } -}", HangMitigatingCancellationToken); + await SetUpEditorAsync(""" + class Program + { + int Main() + { + Foo$$(); + } + } + """, HangMitigatingCancellationToken); await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); var classifiedTokens = await TestServices.Editor.GetLightBulbPreviewClassificationsAsync("Generate method 'Foo'", HangMitigatingCancellationToken); Assert.True(classifiedTokens.Any(c => c.Span.GetText().ToString() == "void" && c.ClassificationType.Classification == "keyword")); @@ -483,16 +454,17 @@ int Main() [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)] public async Task AddUsingExactMatchBeforeRenameTracking() { - await SetUpEditorAsync(@" -public class Program -{ - static void Main(string[] args) - { - P2$$ p; - } -} + await SetUpEditorAsync(""" + public class Program + { + static void Main(string[] args) + { + P2$$ p; + } + } -public class P2 { }", HangMitigatingCancellationToken); + public class P2 { } + """, HangMitigatingCancellationToken); await TestServices.Input.SendAsync([VirtualKeyCode.BACK, VirtualKeyCode.BACK, "Stream"], HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync( @@ -543,22 +515,23 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateType)] public async Task GFUFuzzyMatchAfterRenameTrackingAndAfterGenerateType() { - await SetUpEditorAsync(@" -namespace N -{ - class Goober { } -} + await SetUpEditorAsync(""" + namespace N + { + class Goober { } + } -namespace NS -{ - public class P2 - { - static void Main(string[] args) - { - P2$$ p; - } - } -}", HangMitigatingCancellationToken); + namespace NS + { + public class P2 + { + static void Main(string[] args) + { + P2$$ p; + } + } + } + """, HangMitigatingCancellationToken); await TestServices.Input.SendAsync([VirtualKeyCode.BACK, VirtualKeyCode.BACK, "Foober"], HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync( [ @@ -599,18 +572,19 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( [IdeFact, Trait(Traits.Feature, Traits.Features.CodeGeneration)] public async Task SuppressionAfterRefactorings() { - await SetUpEditorAsync(@" -[System.Obsolete] -class C -{ -} -class Program -{ - static void Main(string[] args) - { - C p = $$2; - } -}", HangMitigatingCancellationToken); + await SetUpEditorAsync(""" + [System.Obsolete] + class C + { + } + class Program + { + static void Main(string[] args) + { + C p = $$2; + } + } + """, HangMitigatingCancellationToken); await TestServices.Editor.SelectTextInCurrentDocumentAsync("2", HangMitigatingCancellationToken); await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); @@ -642,16 +616,17 @@ static void Main(string[] args) [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)] public async Task OrderFixesByCursorProximityLeft() { - await SetUpEditorAsync(@" -using System; -public class Program -{ - static void Main(string[] args) - { - Byte[] bytes = null; - GCHandle$$ handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); - } -}", HangMitigatingCancellationToken); + await SetUpEditorAsync(""" + using System; + public class Program + { + static void Main(string[] args) + { + Byte[] bytes = null; + GCHandle$$ handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); + } + } + """, HangMitigatingCancellationToken); await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); var expectedItems = new[] { @@ -666,16 +641,17 @@ static void Main(string[] args) [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)] public async Task OrderFixesByCursorProximityRight() { - await SetUpEditorAsync(@" -using System; -public class Program -{ - static void Main(string[] args) - { - Byte[] bytes = null; - GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.$$Pinned); - } -}", HangMitigatingCancellationToken); + await SetUpEditorAsync(""" + using System; + public class Program + { + static void Main(string[] args) + { + Byte[] bytes = null; + GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.$$Pinned); + } + } + """, HangMitigatingCancellationToken); await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); var expectedItems = new[] { @@ -690,15 +666,16 @@ static void Main(string[] args) [IdeFact, Trait(Traits.Feature, Traits.Features.CodeActionsConfiguration)] public async Task ConfigureCodeStyleOptionValueAndSeverity() { - await SetUpEditorAsync(@" -using System; -public class Program -{ - static void Main(string[] args) - { - var $$x = new Program(); - } -}", HangMitigatingCancellationToken); + await SetUpEditorAsync(""" + using System; + public class Program + { + static void Main(string[] args) + { + var $$x = new Program(); + } + } + """, HangMitigatingCancellationToken); await TestServices.Editor.InvokeCodeActionListAsync(HangMitigatingCancellationToken); await TestServices.EditorVerifier.CodeActionsAsync([ @@ -714,15 +691,16 @@ await TestServices.EditorVerifier.CodeActionsAsync([ [WorkItem("https://github.com/dotnet/roslyn/issues/46784")] public async Task ConfigureSeverity() { - var markup = @" -class C -{ - public static void Main() - { - // CS0168: The variable 'x' is declared but never used - int $$x; - } -}"; + var markup = """ + class C + { + public static void Main() + { + // CS0168: The variable 'x' is declared but never used + int $$x; + } + } + """; await SetUpEditorAsync(markup, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync( @@ -802,15 +780,16 @@ static async Task VerifyDiagnosticInErrorListAsync(string expectedSeverity, Test [WorkItem("https://github.com/dotnet/roslyn/issues/46784")] public async Task ConfigureSeverityWithManualEditsToEditorconfig() { - var markup = @" -class C -{ - public static void Main() - { - // CS0168: The variable 'x' is declared but never used - int $$x; - } -}"; + var markup = """ + class C + { + public static void Main() + { + // CS0168: The variable 'x' is declared but never used + int $$x; + } + } + """; await SetUpEditorAsync(markup, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync( @@ -827,9 +806,10 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( // Add an .editorconfig file to the project to change severity to error. await TestServices.SolutionExplorer.AddFileAsync(ProjectName, ".editorconfig", open: true, cancellationToken: HangMitigatingCancellationToken); - await TestServices.Input.SendAsync(@" -[*.cs] -dotnet_diagnostic.CS0168.severity = ", HangMitigatingCancellationToken); + await TestServices.Input.SendAsync(""" + [*.cs] + dotnet_diagnostic.CS0168.severity = + """, HangMitigatingCancellationToken); // NOTE: Below wait is a critical step in repro-ing the original regression. await TestServices.Workspace.WaitForAllAsyncOperationsAsync( @@ -884,27 +864,29 @@ static async Task VerifyDiagnosticInErrorListAsync(string expectedSeverity, Test [InlineData(BackgroundAnalysisScope.FullSolution, CompilerDiagnosticsScope.FullSolution)] internal async Task ConfigureSeverityWithManualEditsToEditorconfig_CurrentDocumentScope(BackgroundAnalysisScope analyzerScope, CompilerDiagnosticsScope compilerScope) { - var markup1 = @" -class C -{ - public static void Main() - { - // CS0219: The variable 'x' is assigned but its value is never used - // IDE0059: Unnecessary assignment of a value to 'x' - int x = 0; - } -}"; + var markup1 = """ + class C + { + public static void Main() + { + // CS0219: The variable 'x' is assigned but its value is never used + // IDE0059: Unnecessary assignment of a value to 'x' + int x = 0; + } + } + """; - var markup2 = @" -class C2 -{ - public static void M() - { - // CS0219: The variable 'y' is assigned but its value is never used - // IDE0059: Unnecessary assignment of a value to 'y' - int $$y = 0; - } -}"; + var markup2 = """ + class C2 + { + public static void M() + { + // CS0219: The variable 'y' is assigned but its value is never used + // IDE0059: Unnecessary assignment of a value to 'y' + int $$y = 0; + } + } + """; await TestServices.Workspace.SetBackgroundAnalysisOptionsAsync(analyzerScope, compilerScope, HangMitigatingCancellationToken); await SetUpEditorAsync(markup2, HangMitigatingCancellationToken); @@ -934,10 +916,11 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( // Add an .editorconfig file to the project to change severities to error. await TestServices.SolutionExplorer.AddFileAsync(ProjectName, ".editorconfig", open: true, cancellationToken: HangMitigatingCancellationToken); - await TestServices.Editor.SetTextAsync(@" -[*.cs] -dotnet_diagnostic.CS0219.severity = error -dotnet_diagnostic.IDE0059.severity = error", HangMitigatingCancellationToken); + await TestServices.Editor.SetTextAsync(""" + [*.cs] + dotnet_diagnostic.CS0219.severity = error + dotnet_diagnostic.IDE0059.severity = error + """, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync( [ @@ -952,10 +935,11 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( await VerifyDiagnosticsInErrorListAsync("error", "error", TestServices, HangMitigatingCancellationToken); // Edit editorconfig file to disable both compiler and analyzer diagnostics. - await TestServices.Editor.SetTextAsync(@" -[*.cs] -dotnet_diagnostic.CS0219.severity = none -dotnet_diagnostic.IDE0059.severity = none", HangMitigatingCancellationToken); + await TestServices.Editor.SetTextAsync(""" + [*.cs] + dotnet_diagnostic.CS0219.severity = none + dotnet_diagnostic.IDE0059.severity = none + """, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync( [ @@ -1010,64 +994,66 @@ static async Task VerifyDiagnosticsInErrorListAsync(string expectedCompilerDiagn [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] public async Task TestFixAllOccurrences_CodeFix_ContainingMember() { - var markup = @" -class Program1 -{ - static void Main() - { - $$if (true) if (true) return; + var markup = """ + class Program1 + { + static void Main() + { + $$if (true) if (true) return; - if (false) if (false) return; - } + if (false) if (false) return; + } - void OtherMethod() - { - if (true) if (true) return; - } -} + void OtherMethod() + { + if (true) if (true) return; + } + } -class OtherType -{ - void OtherMethod() - { - if (true) if (true) return; - } -}"; - var expectedText = @" -class Program1 -{ - static void Main() - { - if (true) - { - if (true) + class OtherType { - return; + void OtherMethod() + { + if (true) if (true) return; + } } - } - - if (false) - { - if (false) + """; + var expectedText = """ + class Program1 { - return; + static void Main() + { + if (true) + { + if (true) + { + return; + } + } + + if (false) + { + if (false) + { + return; + } + } + } + + void OtherMethod() + { + if (true) if (true) return; + } } - } - } - void OtherMethod() - { - if (true) if (true) return; - } -} - -class OtherType -{ - void OtherMethod() - { - if (true) if (true) return; - } -}"; + class OtherType + { + void OtherMethod() + { + if (true) if (true) return; + } + } + """; await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken); @@ -1097,141 +1083,145 @@ await TestServices.EditorVerifier.CodeActionAsync( [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] public async Task TestFixAllOccurrences_CodeFix_ContainingType() { - var markup1 = @" -partial class Program1 -{ - static void Main() - { - $$if (true) if (true) return; - - if (false) if (false) return; - } + var markup1 = """ + partial class Program1 + { + static void Main() + { + $$if (true) if (true) return; - void M1() - { - if (true) if (true) return; - } -} + if (false) if (false) return; + } -class OtherType1 -{ - void OtherMethod() - { - if (true) if (true) return; - } -} + void M1() + { + if (true) if (true) return; + } + } -partial class Program1 -{ - void M2() - { - if (true) if (true) return; - } -}"; - var expectedText1 = @" -partial class Program1 -{ - static void Main() - { - if (true) - { - if (true) + class OtherType1 { - return; + void OtherMethod() + { + if (true) if (true) return; + } } - } - if (false) - { - if (false) + partial class Program1 { - return; + void M2() + { + if (true) if (true) return; + } } - } - } - - void M1() - { - if (true) - { - if (true) + """; + var expectedText1 = """ + partial class Program1 { - return; + static void Main() + { + if (true) + { + if (true) + { + return; + } + } + + if (false) + { + if (false) + { + return; + } + } + } + + void M1() + { + if (true) + { + if (true) + { + return; + } + } + } } - } - } -} -class OtherType1 -{ - void OtherMethod() - { - if (true) if (true) return; - } -} + class OtherType1 + { + void OtherMethod() + { + if (true) if (true) return; + } + } -partial class Program1 -{ - void M2() - { - if (true) - { - if (true) + partial class Program1 { - return; + void M2() + { + if (true) + { + if (true) + { + return; + } + } + } } - } - } -}"; + """; - var markup2 = @" -partial class Program1 -{ - void OtherFileMethod() - { - if (true) if (true) return; + var markup2 = """ + partial class Program1 + { + void OtherFileMethod() + { + if (true) if (true) return; - if (false) if (false) return; - } -} + if (false) if (false) return; + } + } -class OtherType2 -{ - void OtherMethod() - { - if (true) if (true) return; - } -}"; - var expectedText2 = @" -partial class Program1 -{ - void OtherFileMethod() - { - if (true) - { - if (true) + class OtherType2 { - return; + void OtherMethod() + { + if (true) if (true) return; + } } - } - - if (false) - { - if (false) + """; + var expectedText2 = """ + partial class Program1 { - return; + void OtherFileMethod() + { + if (true) + { + if (true) + { + return; + } + } + + if (false) + { + if (false) + { + return; + } + } + } } - } - } -} -class OtherType2 -{ - void OtherMethod() - { - if (true) if (true) return; - } -}"; + class OtherType2 + { + void OtherMethod() + { + if (true) if (true) return; + } + } + """; await TestServices.SolutionExplorer.AddFileAsync(ProjectName, "Class2.cs", markup2, cancellationToken: HangMitigatingCancellationToken); await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken); @@ -1264,54 +1254,56 @@ await TestServices.EditorVerifier.CodeActionAsync( [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] public async Task TestFixAllOccurrences_CodeRefactoring_ContainingMember() { - var markup = @" -class C1 -{ - void M() - { - var singleLine1 = $$""a""; - var singleLine2 = @""goo""""bar""; - } - - void M2() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -} - -class C2 -{ - void M3() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -}"; - var expectedText = @" -class C1 -{ - void M() - { - var singleLine1 = """"""a""""""; - var singleLine2 = """"""goo""bar""""""; - } + var markup = """ + class C1 + { + void M() + { + var singleLine1 = $$"a"; + var singleLine2 = @"goo""bar"; + } + + void M2() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } - void M2() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -} + class C2 + { + void M3() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } + """; + var expectedText = """" + class C1 + { + void M() + { + var singleLine1 = """a"""; + var singleLine2 = """goo"bar"""; + } + + void M2() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } -class C2 -{ - void M3() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -}"; + class C2 + { + void M3() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } + """"; await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken); @@ -1341,109 +1333,113 @@ await TestServices.EditorVerifier.CodeActionAsync( [Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)] public async Task TestFixAllOccurrences_CodeRefactoring_ContainingType() { - var markup1 = @" -partial class C1 -{ - void M() - { - var singleLine1 = $$""a""; - var singleLine2 = @""goo""""bar""; - } - - void M2() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -} - -class C2 -{ - void M3() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -} + var markup1 = """ + partial class C1 + { + void M() + { + var singleLine1 = $$"a"; + var singleLine2 = @"goo""bar"; + } + + void M2() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } -partial class C1 -{ - void M4() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -}"; - var expectedText1 = @" -partial class C1 -{ - void M() - { - var singleLine1 = """"""a""""""; - var singleLine2 = """"""goo""bar""""""; - } + class C2 + { + void M3() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } - void M2() - { - var singleLine1 = """"""a""""""; - var singleLine2 = """"""goo""bar""""""; - } -} + partial class C1 + { + void M4() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } + """; + var expectedText1 = """" + partial class C1 + { + void M() + { + var singleLine1 = """a"""; + var singleLine2 = """goo"bar"""; + } + + void M2() + { + var singleLine1 = """a"""; + var singleLine2 = """goo"bar"""; + } + } -class C2 -{ - void M3() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -} + class C2 + { + void M3() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } -partial class C1 -{ - void M4() - { - var singleLine1 = """"""a""""""; - var singleLine2 = """"""goo""bar""""""; - } -}"; + partial class C1 + { + void M4() + { + var singleLine1 = """a"""; + var singleLine2 = """goo"bar"""; + } + } + """"; - var markup2 = @" -partial class C1 -{ - void M5() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -} + var markup2 = """ + partial class C1 + { + void M5() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } -class C2 -{ - void M6() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -}"; - var expectedText2 = @" -partial class C1 -{ - void M5() - { - var singleLine1 = """"""a""""""; - var singleLine2 = """"""goo""bar""""""; - } -} + class C2 + { + void M6() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } + """; + var expectedText2 = """" + partial class C1 + { + void M5() + { + var singleLine1 = """a"""; + var singleLine2 = """goo"bar"""; + } + } -class C2 -{ - void M6() - { - var singleLine1 = ""a""; - var singleLine2 = @""goo""""bar""; - } -}"; + class C2 + { + void M6() + { + var singleLine1 = "a"; + var singleLine2 = @"goo""bar"; + } + } + """"; await TestServices.SolutionExplorer.AddFileAsync(ProjectName, "Class2.cs", markup2, cancellationToken: HangMitigatingCancellationToken); await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken); @@ -1476,15 +1472,15 @@ await TestServices.EditorVerifier.CodeActionAsync( [WorkItem("https://github.com/dotnet/roslyn/issues/61334")] public async Task UseExpressionBodyBeforeExtractBaseClass() { - await SetUpEditorAsync(@" -public class Program -{ - $$public void M() - { - System.Console.WriteLine(0); - } -} -", HangMitigatingCancellationToken); + await SetUpEditorAsync(""" + public class Program + { + $$public void M() + { + System.Console.WriteLine(0); + } + } + """, HangMitigatingCancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync( [ @@ -1517,8 +1513,10 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( public async Task TestNonSourceDocumentRefactoring() { var markup = @"$$# Editorconfig File"; - var expectedText = @"# Editorconfig File -# Refactored"; + var expectedText = """ + # Editorconfig File + # Refactored + """; await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "Class1.cs", HangMitigatingCancellationToken); await TestServices.SolutionExplorer.AddAnalyzerReferenceAsync(ProjectName, typeof(NonSourceFileRefactoring).Assembly.Location, HangMitigatingCancellationToken); @@ -1548,14 +1546,15 @@ await TestServices.EditorVerifier.CodeActionAsync( [IdeFact, Trait(Traits.Feature, Traits.Features.CodeGeneration)] public async Task TestRefactoringsAreSortedByPriority() { - var codeFormat = @" -#pragma warning disable IDE0060 // Remove unused parameter -class C -{ - public C(int x1, int x2, int x3) - { - } -};"; + var codeFormat = """ + #pragma warning disable IDE0060 // Remove unused parameter + class C + { + public C(int x1, int x2, int x3) + { + } + }; + """; for (var i = 1; i <= 3; i++) { var code = codeFormat.Replace($"x{i}", $"$$x{i}"); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRedirectFeaturesAnalyzers.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRedirectFeaturesAnalyzers.cs index 9be3c3b789c61..9508f418a39bc 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRedirectFeaturesAnalyzers.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRedirectFeaturesAnalyzers.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -9,6 +10,8 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Shell.TableManager; using Roslyn.Test.Utilities; using Roslyn.VisualStudio.IntegrationTests; using Roslyn.VisualStudio.NewIntegrationTests.InProcess; @@ -86,7 +89,6 @@ await TestServices.SolutionExplorer.AddCustomProjectAsync( """, cancellationToken); - await TestServices.SolutionExplorer.RestoreNuGetPackagesAsync(ProjectName, cancellationToken); // Configure the global indentation size which would be part of the Host fallback options. var globalOptions = await TestServices.Shell.GetComponentModelServiceAsync(cancellationToken); @@ -143,7 +145,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( FeatureAttribute.ErrorList ], cancellationToken); - return await TestServices.ErrorList.GetErrorsAsync(cancellationToken); + return await TestServices.ErrorList.GetErrorsAsync(ErrorSource.Other, __VSERRORCATEGORY.EC_WARNING, cancellationToken); } private async Task WaitForCodeActionListToPopulateAsync(CancellationToken cancellationToken) @@ -151,6 +153,8 @@ private async Task WaitForCodeActionListToPopulateAsync(CancellationToken cancel await TestServices.Editor.ActivateAsync(cancellationToken); await TestServices.Editor.PlaceCaretAsync("void M()", charsOffset: -1, cancellationToken); + await Task.Delay(TimeSpan.FromSeconds(1)); + await TestServices.Editor.InvokeCodeActionListAsync(cancellationToken); await TestServices.Workspace.WaitForAllAsyncOperationsAsync( diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/StateResetInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/StateResetInProcess.cs index cce8b434279f0..9eac00fa11aa0 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/StateResetInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/StateResetInProcess.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Editor.InlineRename; using Microsoft.CodeAnalysis.Editor.Options; using Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.InheritanceMargin; using Microsoft.CodeAnalysis.InlineRename; @@ -63,6 +64,7 @@ public async Task ResetGlobalOptionsAsync(CancellationToken cancellationToken) ResetOption(globalOptions, MetadataAsSourceOptionsStorage.NavigateToDecompiledSources); ResetOption(globalOptions, WorkspaceConfigurationOptionsStorage.SourceGeneratorExecution); ResetOption(globalOptions, WorkspaceConfigurationOptionsStorage.SourceGeneratorExecutionBalancedFeatureFlag); + ResetPerLanguageOption(globalOptions, FormattingOptions2.IndentationSize); ResetPerLanguageOption(globalOptions, BlockStructureOptionsStorage.CollapseSourceLinkEmbeddedDecompiledFilesWhenFirstOpened); ResetPerLanguageOption(globalOptions, CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces); ResetPerLanguageOption(globalOptions, CompletionOptionsStorage.TriggerInArgumentLists); diff --git a/src/VisualStudio/TestUtilities2/MockComponentModel.vb b/src/VisualStudio/TestUtilities2/MockComponentModel.vb index 5ba9c94a9d75c..81e015d26e91d 100644 --- a/src/VisualStudio/TestUtilities2/MockComponentModel.vb +++ b/src/VisualStudio/TestUtilities2/MockComponentModel.vb @@ -18,13 +18,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests _exportProvider = exportProvider End Sub -#Disable Warning BC40000 ' Type or member is obsolete + Public ReadOnly Property DefaultCatalog As ComposablePartCatalog Implements IComponentModel.DefaultCatalog Get Throw New NotImplementedException End Get End Property -#Enable Warning BC40000 ' Type or member is obsolete Public ReadOnly Property DefaultCompositionService As ICompositionService Implements IComponentModel.DefaultCompositionService Get @@ -38,6 +37,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests End Get End Property + Public Function GetCatalog(catalogName As String) As ComposablePartCatalog Implements IComponentModel.GetCatalog Throw New NotImplementedException End Function diff --git a/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/EntryPointFinder.vb b/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicEntryPointFinder.vb similarity index 77% rename from src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/EntryPointFinder.vb rename to src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicEntryPointFinder.vb index 5103491c8ea0b..902e3feaa8b19 100644 --- a/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/EntryPointFinder.vb +++ b/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicEntryPointFinder.vb @@ -6,13 +6,14 @@ Imports Microsoft.CodeAnalysis Imports Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim - Friend Class EntryPointFinder + Friend NotInheritable Class VisualBasicEntryPointFinder Inherits AbstractEntryPointFinder Private ReadOnly _findFormsOnly As Boolean - Public Sub New(findFormsOnly As Boolean) - Me._findFormsOnly = findFormsOnly + Public Sub New(compilation As Compilation, findFormsOnly As Boolean) + MyBase.New(compilation) + _findFormsOnly = findFormsOnly End Sub Protected Overrides Function MatchesMainMethodName(name As String) As Boolean @@ -23,8 +24,10 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim Return String.Equals(name, "Main", StringComparison.OrdinalIgnoreCase) End Function - Public Shared Function FindEntryPoints(symbol As INamespaceSymbol, findFormsOnly As Boolean) As IEnumerable(Of INamedTypeSymbol) - Dim visitor = New EntryPointFinder(findFormsOnly) + Public Shared Function FindEntryPoints(compilation As Compilation, findFormsOnly As Boolean) As IEnumerable(Of INamedTypeSymbol) + Dim visitor = New VisualBasicEntryPointFinder(compilation, findFormsOnly) + Dim symbol = compilation.SourceModule.GlobalNamespace + ' Attempt to only search source symbols ' Some callers will give a symbol that is not part of a compilation If symbol.ContainingCompilation IsNot Nothing Then @@ -49,6 +52,5 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim MyBase.VisitNamedType(symbol) End Sub - End Class End Namespace diff --git a/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicEntryPointFinderService.vb b/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicEntryPointFinderService.vb index 3c2b211c7b435..b2ddf22cc26a3 100644 --- a/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicEntryPointFinderService.vb +++ b/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicEntryPointFinderService.vb @@ -9,16 +9,16 @@ Imports Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim - Friend Class VisualBasicEntryPointFinderService - Implements IEntryPointFinderService + Friend NotInheritable Class VisualBasicEntryPointFinderService + Inherits AbstractEntryPointFinderService Public Sub New() End Sub - Public Function FindEntryPoints(symbol As INamespaceSymbol, findFormsOnly As Boolean) As IEnumerable(Of INamedTypeSymbol) Implements IEntryPointFinderService.FindEntryPoints - Return EntryPointFinder.FindEntryPoints(symbol, findFormsOnly) + Protected Overrides Function FindEntryPoints(compilation As Compilation, findFormsOnly As Boolean) As IEnumerable(Of INamedTypeSymbol) + Return VisualBasicEntryPointFinder.FindEntryPoints(compilation, findFormsOnly) End Function End Class End Namespace diff --git a/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicProject.vb b/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicProject.vb index bbea3285c113a..0b6512d5a454f 100644 --- a/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicProject.vb +++ b/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicProject.vb @@ -210,7 +210,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim ByVal pcActualItems As IntPtr, findFormsOnly As Boolean) - Dim entryPoints = EntryPointFinder.FindEntryPoints(compilation.SourceModule.GlobalNamespace, findFormsOnly:=findFormsOnly) + Dim entryPoints = VisualBasicEntryPointFinder.FindEntryPoints(compilation, findFormsOnly:=findFormsOnly) ' If called with cItems = 0 and pcActualItems != NULL, GetEntryPointsList returns in pcActualItems the number of items available. If cItems = 0 AndAlso pcActualItems <> Nothing Then diff --git a/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs b/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs index 2ab98f79a04e1..f55c86feaaedc 100644 --- a/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs +++ b/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs @@ -59,39 +59,35 @@ internal static class ClassificationHelpers } private static bool IsControlKeyword(SyntaxToken token) - { - if (token.Parent is null || !IsControlKeywordKind(token.Kind())) - { - return false; - } - - return IsControlStatementKind(token.Parent.Kind()); - } + => token.Parent is not null && + IsControlKeywordKind(token.Kind()) && + IsControlStatementKind(token.Parent.Kind()); private static bool IsControlKeywordKind(SyntaxKind kind) { switch (kind) { - case SyntaxKind.IfKeyword: - case SyntaxKind.ElseKeyword: - case SyntaxKind.WhileKeyword: - case SyntaxKind.ForKeyword: - case SyntaxKind.ForEachKeyword: - case SyntaxKind.DoKeyword: - case SyntaxKind.SwitchKeyword: + case SyntaxKind.AwaitKeyword: + case SyntaxKind.BreakKeyword: case SyntaxKind.CaseKeyword: - case SyntaxKind.TryKeyword: case SyntaxKind.CatchKeyword: + case SyntaxKind.ContinueKeyword: + case SyntaxKind.DefaultKeyword: // Include DefaultKeyword as it can be part of a DefaultSwitchLabel + case SyntaxKind.DoKeyword: + case SyntaxKind.ElseKeyword: case SyntaxKind.FinallyKeyword: + case SyntaxKind.ForEachKeyword: + case SyntaxKind.ForKeyword: case SyntaxKind.GotoKeyword: - case SyntaxKind.BreakKeyword: - case SyntaxKind.ContinueKeyword: + case SyntaxKind.IfKeyword: + case SyntaxKind.InKeyword: // Include InKeyword as it can be part of an ForEachStatement case SyntaxKind.ReturnKeyword: + case SyntaxKind.SwitchKeyword: case SyntaxKind.ThrowKeyword: - case SyntaxKind.YieldKeyword: - case SyntaxKind.DefaultKeyword: // Include DefaultKeyword as it can be part of a DefaultSwitchLabel - case SyntaxKind.InKeyword: // Include InKeyword as it can be part of an ForEachStatement + case SyntaxKind.TryKeyword: case SyntaxKind.WhenKeyword: // Include WhenKeyword as it can be part of a CatchFilterClause or a pattern WhenClause + case SyntaxKind.WhileKeyword: + case SyntaxKind.YieldKeyword: return true; default: return false; @@ -103,34 +99,35 @@ private static bool IsControlStatementKind(SyntaxKind kind) switch (kind) { // Jump Statements - case SyntaxKind.GotoStatement: - case SyntaxKind.GotoCaseStatement: - case SyntaxKind.GotoDefaultStatement: case SyntaxKind.BreakStatement: case SyntaxKind.ContinueStatement: - case SyntaxKind.ReturnStatement: - case SyntaxKind.YieldReturnStatement: - case SyntaxKind.YieldBreakStatement: - case SyntaxKind.ThrowStatement: - case SyntaxKind.WhileStatement: case SyntaxKind.DoStatement: - case SyntaxKind.ForStatement: case SyntaxKind.ForEachStatement: case SyntaxKind.ForEachVariableStatement: + case SyntaxKind.ForStatement: + case SyntaxKind.GotoCaseStatement: + case SyntaxKind.GotoDefaultStatement: + case SyntaxKind.GotoStatement: + case SyntaxKind.ReturnStatement: + case SyntaxKind.ThrowStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.YieldBreakStatement: + case SyntaxKind.YieldReturnStatement: // Checked Statements - case SyntaxKind.IfStatement: - case SyntaxKind.ElseClause: - case SyntaxKind.SwitchStatement: - case SyntaxKind.SwitchSection: - case SyntaxKind.CaseSwitchLabel: + case SyntaxKind.AwaitExpression: case SyntaxKind.CasePatternSwitchLabel: - case SyntaxKind.DefaultSwitchLabel: - case SyntaxKind.TryStatement: + case SyntaxKind.CaseSwitchLabel: case SyntaxKind.CatchClause: case SyntaxKind.CatchFilterClause: + case SyntaxKind.DefaultSwitchLabel: + case SyntaxKind.ElseClause: case SyntaxKind.FinallyClause: + case SyntaxKind.IfStatement: case SyntaxKind.SwitchExpression: + case SyntaxKind.SwitchSection: + case SyntaxKind.SwitchStatement: case SyntaxKind.ThrowExpression: + case SyntaxKind.TryStatement: case SyntaxKind.WhenClause: return true; default: diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index 95dc4d1a23c5d..781feb87fa332 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.CSharp.LanguageService; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -190,9 +191,7 @@ public override SyntaxNode FieldDeclaration( private protected override SyntaxNode ParameterDeclaration( string name, SyntaxNode? type, SyntaxNode? initializer, RefKind refKind, bool isExtension, bool isParams, bool isScoped) { - var modifiers = CSharpSyntaxGeneratorInternal.GetParameterModifiers(refKind); - if (isScoped) - modifiers = modifiers.Insert(0, ScopedKeyword); + var modifiers = CSharpSyntaxGeneratorInternal.GetParameterModifiers(isScoped, refKind); if (isExtension) modifiers = modifiers.Insert(0, ThisKeyword); @@ -2499,38 +2498,27 @@ SyntaxKind.AddAccessorDeclaration or public override SyntaxNode WithStatements(SyntaxNode declaration, IEnumerable statements) { - var body = CreateBlock(statements); + var existingBlock = declaration switch + { + BaseMethodDeclarationSyntax baseMethod => baseMethod.Body, + AccessorDeclarationSyntax accessor => accessor.Body, + LocalFunctionStatementSyntax localFunction => localFunction.Body, + AnonymousFunctionExpressionSyntax anonymousFunction => anonymousFunction.Block, + _ => null, + }; + + var body = CreateBlock(statements, existingBlock, addSimplifierAnnotation: false); var somebody = statements != null ? body : null; var semicolon = statements == null ? SemicolonToken : default; - switch (declaration.Kind()) + return declaration switch { - case SyntaxKind.MethodDeclaration: - return ((MethodDeclarationSyntax)declaration).WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null); - case SyntaxKind.OperatorDeclaration: - return ((OperatorDeclarationSyntax)declaration).WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null); - case SyntaxKind.ConversionOperatorDeclaration: - return ((ConversionOperatorDeclarationSyntax)declaration).WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null); - case SyntaxKind.ConstructorDeclaration: - return ((ConstructorDeclarationSyntax)declaration).WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null); - case SyntaxKind.DestructorDeclaration: - return ((DestructorDeclarationSyntax)declaration).WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null); - case SyntaxKind.LocalFunctionStatement: - return ((LocalFunctionStatementSyntax)declaration).WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null); - case SyntaxKind.AnonymousMethodExpression: - return ((AnonymousMethodExpressionSyntax)declaration).WithBody(body); - case SyntaxKind.ParenthesizedLambdaExpression: - return ((ParenthesizedLambdaExpressionSyntax)declaration).WithBody(body); - case SyntaxKind.SimpleLambdaExpression: - return ((SimpleLambdaExpressionSyntax)declaration).WithBody(body); - case SyntaxKind.GetAccessorDeclaration: - case SyntaxKind.SetAccessorDeclaration: - case SyntaxKind.AddAccessorDeclaration: - case SyntaxKind.RemoveAccessorDeclaration: - return ((AccessorDeclarationSyntax)declaration).WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null); - default: - return declaration; - } + BaseMethodDeclarationSyntax baseMethod => baseMethod.WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null), + AccessorDeclarationSyntax accessor => accessor.WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null), + LocalFunctionStatementSyntax localFunction => localFunction.WithBody(somebody).WithSemicolonToken(semicolon).WithExpressionBody(null), + AnonymousFunctionExpressionSyntax anonymousFunction => anonymousFunction.WithBody(body), + _ => declaration, + }; } public override IReadOnlyList GetAccessors(SyntaxNode declaration) @@ -3134,8 +3122,23 @@ public override SyntaxNode IfStatement(SyntaxNode condition, IEnumerable? statements = null) - => SyntaxFactory.Block(AsStatementList(statements)).WithAdditionalAnnotations(Simplifier.Annotation); + private static BlockSyntax CreateBlock( + IEnumerable? statements = null, + BlockSyntax? existingBlock = null, + bool addSimplifierAnnotation = true) + { + var block = existingBlock ?? SyntaxFactory.Block(); + + var statementList = AsStatementList(statements); + + // If we're adding any statements, make sure the open brace can move around. This allows `{ }` on an existing + // one-line construct to have the braces move to their own lines in accordance to the user's formatting rules. + if (statementList.Count > 0) + block = block.WithOpenBraceToken(block.OpenBraceToken.WithAdditionalAnnotations(Formatter.Annotation)); + + block = block.WithStatements(statementList); + return addSimplifierAnnotation ? block.WithAdditionalAnnotations(Simplifier.Annotation) : block; + } private static SyntaxList AsStatementList(IEnumerable? nodes) => nodes == null ? default : [.. nodes.Select(AsStatement)]; diff --git a/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs b/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs index eb36500f4286c..f3f3c44bea3cc 100644 --- a/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs +++ b/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs @@ -23,26 +23,20 @@ namespace Microsoft.CodeAnalysis.CSharp.Formatting; -internal sealed class CSharpSyntaxFormattingService : CSharpSyntaxFormatting, ISyntaxFormattingService +internal sealed class CSharpSyntaxFormattingService(LanguageServices languageServices) + : CSharpSyntaxFormatting, ISyntaxFormattingService { - private readonly LanguageServices _services; + private readonly LanguageServices _services = languageServices; [ExportLanguageServiceFactory(typeof(ISyntaxFormattingService), LanguageNames.CSharp), Shared] - internal sealed class Factory : ILanguageServiceFactory + [method: ImportingConstructor] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + internal sealed class Factory() : ILanguageServiceFactory { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public Factory() - { - } - public ILanguageService CreateLanguageService(HostLanguageServices languageServices) => new CSharpSyntaxFormattingService(languageServices.LanguageServices); } - private CSharpSyntaxFormattingService(LanguageServices languageServices) - => _services = languageServices; - public bool ShouldFormatOnTypedCharacter( ParsedDocument documentSyntax, char typedChar, @@ -59,31 +53,19 @@ public bool ShouldFormatOnTypedCharacter( return false; } - // If the token is a ) we only want to format if it's the close paren - // of a using statement. That way if we have nested usings, the inner - // using will align with the outer one when the user types the close paren. + // If the token is a ) we only want to format if it's the close paren of a using statement. That way if we + // have nested usings, the inner using will align with the outer one when the user types the close paren. if (token.IsKind(SyntaxKind.CloseParenToken) && !token.Parent.IsKind(SyntaxKind.UsingStatement)) - { return false; - } - // If the token is a : we only want to format if it's a labeled statement - // or case. When the colon is typed we'll want ot immediately have those - // statements snap to their appropriate indentation level. - if (token.IsKind(SyntaxKind.ColonToken) && !(token.Parent.IsKind(SyntaxKind.LabeledStatement) || token.Parent is SwitchLabelSyntax)) - { + // If the token is a : we only want to format if it's a labeled statement or case. When the colon is typed + // we'll want ot immediately have those statements snap to their appropriate indentation level. + if (token.IsKind(SyntaxKind.ColonToken) && !token.Parent.IsKind(SyntaxKind.LabeledStatement) && token.Parent is not SwitchLabelSyntax) return false; - } - // Only format an { if it is the first token on a line. We don't want to - // mess with it if it's inside a line. - if (token.IsKind(SyntaxKind.OpenBraceToken)) - { - if (!token.IsFirstTokenOnLine(documentSyntax.Text)) - { - return false; - } - } + // Only format an { if it is the first token on a line. We don't want to mess with it if it's inside a line. + if (token.IsKind(SyntaxKind.OpenBraceToken) && !token.IsFirstTokenOnLine(documentSyntax.Text)) + return false; return true; } @@ -98,43 +80,41 @@ public ImmutableArray GetFormattingChangesOnTypedCharacter( var token = root.FindToken(Math.Max(0, caretPosition - 1), findInsideTrivia: true); var formattingRules = GetFormattingRules(document, caretPosition, token); - // Do not attempt to format on open/close brace if autoformat on close brace feature is - // off, instead just smart indent. + // Do not attempt to format on open/close brace if autoformat on close brace feature is off, instead just smart + // indent. // - // We want this behavior because it's totally reasonable for a user to want to not have - // on automatic formatting because they feel it is too aggressive. However, by default, - // if you have smart-indentation on and are just hitting enter, you'll common have the - // caret placed one indent higher than your current construct. For example, if you have: + // We want this behavior because it's totally reasonable for a user to want to not have on automatic formatting + // because they feel it is too aggressive. However, by default, if you have smart-indentation on and are just + // hitting enter, you'll common have the caret placed one indent higher than your current construct. For + // example, if you have: // // if (true) // $ <-- smart indent will have placed the caret here here. // - // This is acceptable given that the user may want to just write a simple statement there. - // However, if they start writing `{`, then things should snap over to be: + // This is acceptable given that the user may want to just write a simple statement there. However, if they + // start writing `{`, then things should snap over to be: // // if (true) // { // - // Importantly, this is just an indentation change, no actual 'formatting' is done. We do - // the same with close brace. If you have: + // Importantly, this is just an indentation change, no actual 'formatting' is done. We do the same with close + // brace. If you have: // // if (...) // { // bad . ly ( for (mmated+code) ) ; // $ <-- smart indent will have placed the care here. // - // If the user hits `}` then we will properly smart indent the `}` to match the `{`. - // However, we won't touch any of the other code in that block, unlike if we were - // formatting. + // If the user hits `}` then we will properly smart indent the `}` to match the `{`. However, we won't touch any + // of the other code in that block, unlike if we were formatting. var onlySmartIndent = (token.IsKind(SyntaxKind.CloseBraceToken) && OnlySmartIndentCloseBrace(indentationOptions.AutoFormattingOptions)) || (token.IsKind(SyntaxKind.OpenBraceToken) && OnlySmartIndentOpenBrace(indentationOptions.AutoFormattingOptions)); if (onlySmartIndent) { - // if we're only doing smart indent, then ignore all edits to this token that occur before - // the span of the token. They're irrelevant and may screw up other code the user doesn't - // want touched. + // if we're only doing smart indent, then ignore all edits to this token that occur before the span of the + // token. They're irrelevant and may screw up other code the user doesn't want touched. var tokenEdits = FormatToken(document, indentationOptions, token, formattingRules, cancellationToken); return tokenEdits.Where(t => t.Span.Start >= token.FullSpan.Start).ToImmutableArray(); } @@ -142,25 +122,23 @@ public ImmutableArray GetFormattingChangesOnTypedCharacter( // if formatting range fails, do format token one at least var changes = FormatRange(document, indentationOptions, token, formattingRules, cancellationToken); if (changes.Length > 0) - { return changes; - } return [.. FormatToken(document, indentationOptions, token, formattingRules, cancellationToken)]; } private static bool OnlySmartIndentCloseBrace(in AutoFormattingOptions options) { - // User does not want auto-formatting (either in general, or for close braces in - // specific). So we only smart indent close braces when typed. + // User does not want auto-formatting (either in general, or for close braces in specific). So we only smart + // indent close braces when typed. return !options.FormatOnCloseBrace || !options.FormatOnTyping; } private static bool OnlySmartIndentOpenBrace(in AutoFormattingOptions options) { - // User does not want auto-formatting . So we only smart indent open braces when typed. - // Note: there is no specific option for controlling formatting on open brace. So we - // don't have the symmetry with OnlySmartIndentCloseBrace. + // User does not want auto-formatting . So we only smart indent open braces when typed. Note: there is no + // specific option for controlling formatting on open brace. So we don't have the symmetry with + // OnlySmartIndentCloseBrace. return !options.FormatOnTyping; } @@ -178,21 +156,15 @@ private static ImmutableArray FormatRange( ImmutableArray formattingRules, CancellationToken cancellationToken) { - if (!IsEndToken(endToken)) - { + if (endToken.IsKind(SyntaxKind.OpenBraceToken)) return []; - } var tokenRange = FormattingRangeHelper.FindAppropriateRange(endToken); if (tokenRange == null || tokenRange.Value.Item1.Equals(tokenRange.Value.Item2)) - { return []; - } if (IsInvalidTokenKind(tokenRange.Value.Item1) || IsInvalidTokenKind(tokenRange.Value.Item2)) - { return []; - } var formatter = new CSharpSmartTokenFormatter(options, formattingRules, (CompilationUnitSyntax)document.Root, document.Text); @@ -202,19 +174,19 @@ private static ImmutableArray FormatRange( private static IEnumerable GetTypingRules(SyntaxToken tokenBeforeCaret) { - // Typing introduces several challenges around formatting. - // Historically we've shipped several triggers that cause formatting to happen directly while typing. - // These include formatting of blocks when '}' is typed, formatting of statements when a ';' is typed, formatting of ```case```s when ':' typed, and many other cases. - // However, formatting during typing can potentially cause problems. This is because the surrounding code may not be complete, - // or may otherwise have syntax errors, and thus edits could have unintended consequences. + // Typing introduces several challenges around formatting. Historically we've shipped several triggers that + // cause formatting to happen directly while typing. These include formatting of blocks when '}' is typed, + // formatting of statements when a ';' is typed, formatting of ```case```s when ':' typed, and many other cases. + // However, formatting during typing can potentially cause problems. This is because the surrounding code may + // not be complete, or may otherwise have syntax errors, and thus edits could have unintended consequences. // - // Because of this, we introduce an extra rule into the set of formatting rules whose purpose is to actually make formatting *more* - // conservative and *less* willing willing to make edits to the tree. - // The primary effect this rule has is to assume that more code is on a single line (and thus should stay that way) - // despite what the tree actually looks like. + // Because of this, we introduce an extra rule into the set of formatting rules whose purpose is to actually + // make formatting *more* conservative and *less* willing willing to make edits to the tree. The primary effect + // this rule has is to assume that more code is on a single line (and thus should stay that way) despite what + // the tree actually looks like. // - // It's ok that this is only during formatting that is caused by an edit because that formatting happens - // implicitly and thus has to be more careful, whereas an explicit format-document call only happens on-demand + // It's ok that this is only during formatting that is caused by an edit because that formatting happens + // implicitly and thus has to be more careful, whereas an explicit format-document call only happens on-demand // and can be more aggressive about what it's doing. // // @@ -236,9 +208,9 @@ private static IEnumerable GetTypingRules(SyntaxToken to // } // ``` // - // During a normal format-document call, this is not what would happen. - // Specifically, because the parser will consume the '}' into the accessor, - // it will think the accessor spans multiple lines, and thus should not stay on a single line. This will produce: + // During a normal format-document call, this is not what would happen. Specifically, because the parser will + // consume the '}' into the accessor, it will think the accessor spans multiple lines, and thus should not stay + // on a single line. This will produce: // // ```c# // class C @@ -251,10 +223,10 @@ private static IEnumerable GetTypingRules(SyntaxToken to // } // ``` // - // Because it's ok for this to format in that fashion if format-document is invoked, - // but should not happen during typing, we insert a specialized rule *only* during typing to try to control this. - // During normal formatting we add 'keep on single line' suppression rules for blocks we find that are on a single line. - // But that won't work since this span is not on a single line: + // Because it's ok for this to format in that fashion if format-document is invoked, but should not happen + // during typing, we insert a specialized rule *only* during typing to try to control this. During normal + // formatting we add 'keep on single line' suppression rules for blocks we find that are on a single line. But + // that won't work since this span is not on a single line: // // ```c# // class C @@ -283,24 +255,18 @@ private static IEnumerable GetTypingRules(SyntaxToken to return [TypingFormattingRule.Instance]; } - private static bool IsEndToken(SyntaxToken endToken) - { - if (endToken.IsKind(SyntaxKind.OpenBraceToken)) - { - return false; - } - - return true; - } - - // We'll autoformat on n, t, e, only if they are the last character of the below - // keywords. - private static bool ValidSingleOrMultiCharactersTokenKind(char typedChar, SyntaxKind kind) + /// + /// We'll autoformat on 'n', 't', 'e', only if they are the last character of the below keywords. + /// + internal static bool ValidSingleOrMultiCharactersTokenKind(char typedChar, SyntaxKind kind) => typedChar switch { 'n' => kind is SyntaxKind.RegionKeyword or SyntaxKind.EndRegionKeyword, - 't' => kind == SyntaxKind.SelectKeyword, - 'e' => kind == SyntaxKind.WhereKeyword, + 't' => kind is SyntaxKind.SelectKeyword, + 'e' => kind is SyntaxKind.WhereKeyword or SyntaxKind.ElseKeyword, + // Note: we only got here because CSharpFormattingInteractionService.SupportsFormattingOnTypedCharacter + // already determined that the user typed a character that should cause formatting. So all other characters + // that get here are things we want to format on (like `;` or `}`). _ => true, }; @@ -334,17 +300,16 @@ public ImmutableArray GetFormattingChangesOnPaste(ParsedDocument doc return [.. result.GetTextChanges(cancellationToken)]; } - internal sealed class PasteFormattingRule : AbstractFormattingRule + private sealed class PasteFormattingRule : AbstractFormattingRule { public override AdjustNewLinesOperation? GetAdjustNewLinesOperation(in SyntaxToken previousToken, in SyntaxToken currentToken, in NextGetAdjustNewLinesOperation nextOperation) { if (currentToken.Parent != null) { var currentTokenParentParent = currentToken.Parent.Parent; - if (currentToken.Kind() == SyntaxKind.OpenBraceToken && currentTokenParentParent != null && - (currentTokenParentParent.Kind() == SyntaxKind.SimpleLambdaExpression || - currentTokenParentParent.Kind() == SyntaxKind.ParenthesizedLambdaExpression || - currentTokenParentParent.Kind() == SyntaxKind.AnonymousMethodExpression)) + if (currentToken.Kind() == SyntaxKind.OpenBraceToken && + currentTokenParentParent != null && + currentTokenParentParent.Kind() is SyntaxKind.SimpleLambdaExpression or SyntaxKind.ParenthesizedLambdaExpression or SyntaxKind.AnonymousMethodExpression) { return FormattingOperations.CreateAdjustNewLinesOperation(0, AdjustNewLinesOption.PreserveLines); } diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs index fb48ff3897859..f2ed9d5989834 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs @@ -23,17 +23,18 @@ namespace Microsoft.CodeAnalysis.CSharp.Simplification; [ExportLanguageService(typeof(ISimplificationService), LanguageNames.CSharp), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal partial class CSharpSimplificationService() +internal sealed partial class CSharpSimplificationService() : AbstractSimplificationService(s_reducers) { - // 1. the cast simplifier should run earlier then everything else to minimize the type expressions - // 2. Extension method reducer may insert parentheses. So run it before the parentheses remover. + // 1. Prefer 'var' simplification first. In other words we like `var v = (int)x` vs `int v = x` + // 2. the cast simplifier should run earlier then everything else to minimize the type expressions + // 3. Extension method reducer may insert parentheses. So run it before the parentheses remover. private static readonly ImmutableArray s_reducers = [ new CSharpVarReducer(), + new CSharpCastReducer(), new CSharpNameReducer(), new CSharpNullableAnnotationReducer(), - new CSharpCastReducer(), new CSharpExtensionMethodReducer(), new CSharpParenthesizedExpressionReducer(), new CSharpParenthesizedPatternReducer(), diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs index 64164a2955b14..a6668e8b9a77c 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs @@ -5764,8 +5764,7 @@ public async Task PreprocessorOnSameLine() var expected = @"class C { -} -#line default +}#line default #line hidden"; diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj b/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj index e07fe16e8fa57..96805e041d8cb 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj +++ b/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj @@ -43,7 +43,6 @@ - diff --git a/src/Workspaces/Core/Portable/Classification/IRemoteSemanticClassificationService.cs b/src/Workspaces/Core/Portable/Classification/IRemoteSemanticClassificationService.cs index 878923fa5ba8a..70db1c75d1c19 100644 --- a/src/Workspaces/Core/Portable/Classification/IRemoteSemanticClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/IRemoteSemanticClassificationService.cs @@ -69,11 +69,11 @@ internal sealed class SerializableClassifiedSpans(ImmutableArray classif [DataMember(Order = 1)] public readonly ImmutableArray ClassificationTriples = classificationTriples; - internal static SerializableClassifiedSpans Dehydrate(ImmutableArray classifiedSpans) + internal static SerializableClassifiedSpans Dehydrate(SegmentedList classifiedSpans) { using var _1 = PooledDictionary.GetInstance(out var classificationTypeToId); using var _2 = ArrayBuilder.GetInstance(out var classificationTypes); - var classificationTriples = new FixedSizeArrayBuilder(classifiedSpans.Length * 3); + var classificationTriples = new FixedSizeArrayBuilder(classifiedSpans.Count * 3); foreach (var classifiedSpan in classifiedSpans) { diff --git a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs index 70da68cf4cf59..e1dcfdf45f301 100644 --- a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs +++ b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs @@ -328,8 +328,7 @@ public SyntaxNode ParameterDeclaration(IParameterSymbol symbol, SyntaxNode? init symbol.RefKind, isExtension: symbol is { Ordinal: 0, ContainingSymbol: IMethodSymbol { IsExtensionMethod: true } }, symbol.IsParams, - isScoped: symbol is { RefKind: RefKind.Ref or RefKind.In or RefKind.RefReadOnlyParameter, ScopedKind: ScopedKind.ScopedRef } - or { RefKind: RefKind.None, Type.IsRefLikeType: true, ScopedKind: ScopedKind.ScopedValue }); + isScoped: SyntaxGeneratorInternal.ParameterIsScoped(symbol)); } private protected abstract SyntaxNode TypeParameter(ITypeParameterSymbol typeParameter); diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/BaseTypeFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/BaseTypeFinder.cs index 71ab652187128..8af2404b48e03 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/BaseTypeFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/BaseTypeFinder.cs @@ -22,49 +22,59 @@ public static ImmutableArray FindOverriddenAndImplementedMembers( // This is called for all: class, struct or interface member. results.AddRange(symbol.ExplicitOrImplicitInterfaceImplementations()); - // The type scenario. Iterate over all base classes to find overridden and hidden (new/Shadows) methods. - foreach (var type in FindBaseTypes(symbol.ContainingType)) + AddOverrides(allowLooseMatch: false); + + // If we've found nothing at all (either interface impls or exact override matches), then attempt a loose match + // to see if we can find something in an error condition. + if (results.Count == 0) + AddOverrides(allowLooseMatch: true); + + // Remove duplicates from interface implementations before adding their projects. + results.RemoveDuplicates(); + return results.ToImmutableAndClear(); + + void AddOverrides(bool allowLooseMatch) { - foreach (var member in type.GetMembers(symbol.Name)) + // The type scenario. Iterate over all base classes to find overridden and hidden (new/Shadows) methods. + foreach (var type in FindBaseTypes(symbol.ContainingType)) { - cancellationToken.ThrowIfCancellationRequested(); - - // Add to results overridden members only. Do not add hidden members. - if (SymbolFinder.IsOverride(solution, symbol, member)) + foreach (var member in type.GetMembers(symbol.Name)) { - results.Add(member); + cancellationToken.ThrowIfCancellationRequested(); + + // Add to results overridden members only. Do not add hidden members. + if (SymbolFinder.IsOverride(solution, symbol, member, allowLooseMatch)) + { + results.Add(member); - // We should add implementations only for overridden members but not for hidden ones. - // In the following example: - // - // interface I { void M(); } - // class A : I { public void M(); } - // class B : A { public new void M(); } - // - // we should not find anything for B.M() because it does not implement the interface: - // - // I i = new B(); i.M(); - // - // will call the method from A. - // However, if we change the code to - // - // class B : A, I { public new void M(); } - // - // then - // - // I i = new B(); i.M(); - // - // will call the method from B. We should find the base for B.M in this case. - // And if we change 'new' to 'override' in the original code and add 'virtual' where needed, - // we should find I.M as a base for B.M(). And the next line helps with this scenario. - results.AddRange(member.ExplicitOrImplicitInterfaceImplementations()); + // We should add implementations only for overridden members but not for hidden ones. + // In the following example: + // + // interface I { void M(); } + // class A : I { public void M(); } + // class B : A { public new void M(); } + // + // we should not find anything for B.M() because it does not implement the interface: + // + // I i = new B(); i.M(); + // + // will call the method from A. + // However, if we change the code to + // + // class B : A, I { public new void M(); } + // + // then + // + // I i = new B(); i.M(); + // + // will call the method from B. We should find the base for B.M in this case. + // And if we change 'new' to 'override' in the original code and add 'virtual' where needed, + // we should find I.M as a base for B.M(). And the next line helps with this scenario. + results.AddRange(member.ExplicitOrImplicitInterfaceImplementations()); + } } } } - - // Remove duplicates from interface implementations before adding their projects. - results.RemoveDuplicates(); - return results.ToImmutableAndClear(); } private static ImmutableArray FindBaseTypes(INamedTypeSymbol type) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs index 90070f93b4a22..0d8ff1ca6d95b 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs @@ -40,8 +40,9 @@ private static readonly ConditionalWeakTable< public static async Task> GetDependentProjectsAsync( Solution solution, ImmutableArray symbols, IImmutableSet projects, CancellationToken cancellationToken) { - // namespaces are visible in all projects. - if (symbols.Any(static s => s.Kind == SymbolKind.Namespace)) + // Namespaces are visible in all projects. + // Preprocessing symbols are arbitrary identifiers that are not bound to specific projects. + if (symbols.Any(static s => s.Kind is SymbolKind.Namespace or SymbolKind.Preprocessing)) return [.. projects]; var dependentProjects = await GetDependentProjectsWorkerAsync(solution, symbols, cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs new file mode 100644 index 0000000000000..1ab72fbc61b13 --- /dev/null +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.FindSymbols.Finders; + +internal sealed class PreprocessingSymbolReferenceFinder : AbstractReferenceFinder +{ + protected override bool CanFind(IPreprocessingSymbol symbol) + => true; + + protected override async Task DetermineDocumentsToSearchAsync( + IPreprocessingSymbol symbol, + HashSet? globalAliases, + Project project, + IImmutableSet? documents, + Action processResult, + TData processResultData, + FindReferencesSearchOptions options, + CancellationToken cancellationToken) + { + await FindDocumentsWithPredicateAsync( + project, + documents, + index => index.ContainsDirective && index.ProbablyContainsIdentifier(symbol.Name), + processResult, + processResultData, + cancellationToken).ConfigureAwait(false); + } + + protected override void FindReferencesInDocument( + IPreprocessingSymbol symbol, + FindReferencesDocumentState state, + Action processResult, + TData processResultData, + FindReferencesSearchOptions options, + CancellationToken cancellationToken) + { + var tokens = FindMatchingIdentifierTokens(state, symbol.Name, cancellationToken); + + foreach (var token in tokens) + { + cancellationToken.ThrowIfCancellationRequested(); + + var targetSymbol = state.SemanticFacts.GetPreprocessingSymbol(state.SemanticModel, token.GetRequiredParent()); + var matched = SymbolFinder.OriginalSymbolsMatch(state.Solution, symbol, targetSymbol); + + if (matched) + { + var location = CreateFinderLocation(state, token, CandidateReason.None, cancellationToken); + processResult(location, processResultData); + } + } + } +} diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ReferenceFinders.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ReferenceFinders.cs index 58488bb4b9668..60820a4c14a0f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ReferenceFinders.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ReferenceFinders.cs @@ -26,6 +26,7 @@ internal static class ReferenceFinders new OperatorSymbolReferenceFinder(), new OrdinaryMethodReferenceFinder(), new ParameterSymbolReferenceFinder(), + new PreprocessingSymbolReferenceFinder(), new PropertyAccessorSymbolReferenceFinder(), new RangeVariableSymbolReferenceFinder(), new TypeParameterSymbolReferenceFinder(), diff --git a/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs index 30dd5c46447e0..751594f213f6c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs @@ -23,7 +23,7 @@ internal abstract partial class AbstractSyntaxIndex /// that we will not try to read previously cached data from a prior version of roslyn with a different format and /// will instead regenerate all the indices with the new format. /// - private static readonly Checksum s_serializationFormatChecksum = CodeAnalysis.Checksum.Create("42"); + private static readonly Checksum s_serializationFormatChecksum = CodeAnalysis.Checksum.Create("43"); /// /// Cache of ParseOptions to a checksum for the contained diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs index ad70664b74512..b00c9dcb4738a 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Hierarchy.cs @@ -36,17 +36,29 @@ public static async Task> FindOverridesAsync( internal static async Task> FindOverridesArrayAsync( ISymbol symbol, Solution solution, IImmutableSet? projects = null, CancellationToken cancellationToken = default) { - var results = ArrayBuilder.GetInstance(); symbol = symbol.OriginalDefinition; - if (symbol.IsOverridable()) - { - // To find the overrides, we need to walk down the type hierarchy and check all - // derived types. - var containingType = symbol.ContainingType; - var derivedTypes = await FindDerivedClassesAsync( - containingType, solution, projects, cancellationToken).ConfigureAwait(false); + if (!symbol.IsOverridable()) + return []; + + using var _ = ArrayBuilder.GetInstance(out var results); + + // To find the overrides, we need to walk down the type hierarchy and check all + // derived types. + var containingType = symbol.ContainingType; + var derivedTypes = await FindDerivedClassesAsync( + containingType, solution, projects, cancellationToken).ConfigureAwait(false); + // First try finding exact overrides. If that fails to find anything, look for overrides that loosely + // match due to errors. + await FindOverridesAsync(allowLooseMatch: false).ConfigureAwait(false); + if (results.Count == 0) + await FindOverridesAsync(allowLooseMatch: true).ConfigureAwait(false); + + return results.ToImmutableAndClear(); + + async Task FindOverridesAsync(bool allowLooseMatch) + { foreach (var type in derivedTypes) { foreach (var m in type.GetMembers(symbol.Name)) @@ -54,20 +66,20 @@ internal static async Task> FindOverridesArrayAsync( var sourceMember = await FindSourceDefinitionAsync(m, solution, cancellationToken).ConfigureAwait(false); var bestMember = sourceMember ?? m; - if (IsOverride(solution, bestMember, symbol)) + if (IsOverride(solution, bestMember, symbol, allowLooseMatch)) results.Add(bestMember); } } } - - return results.ToImmutableAndFree(); } - internal static bool IsOverride(Solution solution, ISymbol member, ISymbol symbol) + internal static bool IsOverride(Solution solution, ISymbol member, ISymbol symbol, bool allowLooseMatch) { - for (var current = member; current != null; current = current.GetOverriddenMember()) + for (var current = member; + current != null; + current = current.GetOverriddenMember(allowLooseMatch)) { - if (OriginalSymbolsMatch(solution, current.GetOverriddenMember(), symbol.OriginalDefinition)) + if (OriginalSymbolsMatch(solution, current.GetOverriddenMember(allowLooseMatch), symbol.OriginalDefinition)) return true; } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs index cb45509fe84e2..e74a8d9c6e7bc 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs @@ -35,7 +35,8 @@ public ContextInfo( bool containsConversion, bool containsGlobalKeyword, bool containsCollectionInitializer, - bool containsAttribute) + bool containsAttribute, + bool containsDirective) : this(predefinedTypes, predefinedOperators, ConvertToContainingNodeFlag( containsForEachStatement, @@ -54,7 +55,8 @@ public ContextInfo( containsConversion, containsGlobalKeyword, containsCollectionInitializer, - containsAttribute)) + containsAttribute, + containsDirective)) { } @@ -82,7 +84,8 @@ private static ContainingNodes ConvertToContainingNodeFlag( bool containsConversion, bool containsGlobalKeyword, bool containsCollectionInitializer, - bool containsAttribute) + bool containsAttribute, + bool containsDirective) { var containingNodes = ContainingNodes.None; @@ -103,6 +106,7 @@ private static ContainingNodes ConvertToContainingNodeFlag( containingNodes |= containsGlobalKeyword ? ContainingNodes.ContainsGlobalKeyword : 0; containingNodes |= containsCollectionInitializer ? ContainingNodes.ContainsCollectionInitializer : 0; containingNodes |= containsAttribute ? ContainingNodes.ContainsAttribute : 0; + containingNodes |= containsDirective ? ContainingNodes.ContainsDirective : 0; return containingNodes; } @@ -164,6 +168,9 @@ public bool ContainsCollectionInitializer public bool ContainsAttribute => (_containingNodes & ContainingNodes.ContainsAttribute) == ContainingNodes.ContainsAttribute; + public bool ContainsDirective + => (_containingNodes & ContainingNodes.ContainsDirective) == ContainingNodes.ContainsDirective; + public void WriteTo(ObjectWriter writer) { writer.WriteInt32(_predefinedTypes); @@ -209,6 +216,7 @@ private enum ContainingNodes ContainsGlobalKeyword = 1 << 14, ContainsCollectionInitializer = 1 << 15, ContainsAttribute = 1 << 16, + ContainsDirective = 1 << 17, } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs index 044a91e156edf..fc76ff3f3bd5f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs @@ -73,6 +73,7 @@ private static SyntaxTreeIndex CreateIndex( var containsGlobalKeyword = false; var containsCollectionInitializer = false; var containsAttribute = false; + var containsDirective = root.ContainsDirectives; var predefinedTypes = (int)PredefinedType.None; var predefinedOperators = (int)PredefinedOperator.None; @@ -196,7 +197,8 @@ private static SyntaxTreeIndex CreateIndex( containsConversion, containsGlobalKeyword, containsCollectionInitializer, - containsAttribute), + containsAttribute, + containsDirective), aliasInfo, interceptsLocationInfo); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs index b3f75e41db573..c67a2beaf74da 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs @@ -38,6 +38,7 @@ internal sealed partial class SyntaxTreeIndex public bool ContainsUsingStatement => _contextInfo.ContainsUsingStatement; public bool ContainsCollectionInitializer => _contextInfo.ContainsCollectionInitializer; public bool ContainsAttribute => _contextInfo.ContainsAttribute; + public bool ContainsDirective => _contextInfo.ContainsDirective; /// /// Gets the set of global aliases that point to something with the provided name and arity. diff --git a/src/Workspaces/Core/Portable/PatternMatching/PatternMatch.cs b/src/Workspaces/Core/Portable/PatternMatching/PatternMatch.cs index 76af1d62ffc13..3894cc984a121 100644 --- a/src/Workspaces/Core/Portable/PatternMatching/PatternMatch.cs +++ b/src/Workspaces/Core/Portable/PatternMatching/PatternMatch.cs @@ -67,19 +67,37 @@ public int CompareTo(PatternMatch? other, bool ignoreCase) public int CompareTo(PatternMatch other, bool ignoreCase) { - // Compare types - var comparison = this.Kind - other.Kind; - if (comparison != 0) - return comparison; + // 1. In all scenarios, An case sensitive camel match (like CR against CreateRange) should beat a case + // insensitive match (like 'CR' against 'Create'). + // + // Other cases can be added here as necessary. - // Compare cases - if (!ignoreCase) + switch (this.IsCaseSensitive, this.Kind.IsCamelCaseKind(), other.IsCaseSensitive, other.Kind.IsCamelCaseKind()) + { + case (true, true, false, false): + return -1; + case (false, false, true, true): + return 1; + } + + // Compare types { - comparison = (!this.IsCaseSensitive).CompareTo(!other.IsCaseSensitive); + var comparison = this.Kind - other.Kind; if (comparison != 0) return comparison; } + if (!ignoreCase) + { + switch (this.IsCaseSensitive, other.IsCaseSensitive) + { + case (true, false): + return -1; + case (false, true): + return 1; + } + } + // Consider a match to be better if it was successful without stripping punctuation // versus a match that had to strip punctuation to succeed. return this._punctuationStripped.CompareTo(other._punctuationStripped); diff --git a/src/Workspaces/Core/Portable/PatternMatching/PatternMatchKind.cs b/src/Workspaces/Core/Portable/PatternMatching/PatternMatchKind.cs index e3c524344b7f5..d5fefcb9143b4 100644 --- a/src/Workspaces/Core/Portable/PatternMatching/PatternMatchKind.cs +++ b/src/Workspaces/Core/Portable/PatternMatching/PatternMatchKind.cs @@ -107,3 +107,9 @@ internal enum PatternMatchKind /// LowercaseSubstring, } + +internal static class PatternMatchKindExtensions +{ + public static bool IsCamelCaseKind(this PatternMatchKind kind) + => kind is PatternMatchKind.CamelCaseExact or PatternMatchKind.CamelCasePrefix or PatternMatchKind.CamelCaseNonContiguousPrefix or PatternMatchKind.CamelCaseSubstring; +} diff --git a/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.TextChunk.cs b/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.TextChunk.cs index 23b15fec6d7e4..01fbfb1ab3bef 100644 --- a/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.TextChunk.cs +++ b/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.TextChunk.cs @@ -38,6 +38,7 @@ private struct TextChunk : IDisposable public WordSimilarityChecker SimilarityChecker; public readonly bool IsLowercase; + public readonly bool IsUppercase; public TextChunk(string text, bool allowFuzzingMatching) { @@ -50,6 +51,7 @@ public TextChunk(string text, bool allowFuzzingMatching) : default; IsLowercase = !ContainsUpperCaseLetter(text); + IsUppercase = !ContainsLowerCaseLetter(text); } public void Dispose() diff --git a/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs b/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs index 40d789da6019f..96c74e0f71db9 100644 --- a/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs +++ b/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs @@ -114,9 +114,19 @@ private static bool ContainsUpperCaseLetter(string pattern) for (var i = 0; i < pattern.Length; i++) { if (char.IsUpper(pattern[i])) - { return true; - } + } + + return false; + } + + private static bool ContainsLowerCaseLetter(string pattern) + { + // Expansion of "foreach(char ch in pattern)" to avoid a CharEnumerator allocation + for (var i = 0; i < pattern.Length; i++) + { + if (char.IsLower(pattern[i])) + return true; } return false; @@ -153,6 +163,8 @@ private static bool ContainsUpperCaseLetter(string pattern) in TextChunk patternChunk, bool punctuationStripped) { + using var candidateHumps = TemporaryArray.Empty; + var candidateLength = candidate.Length; var caseInsensitiveIndex = _compareInfo.IndexOf(candidate, patternChunk.Text, CompareOptions.IgnoreCase); @@ -170,15 +182,31 @@ private static bool ContainsUpperCaseLetter(string pattern) } else { + var isCaseSensitive = _compareInfo.IsPrefix(candidate, patternChunk.Text); + + if (!isCaseSensitive && patternChunk.IsUppercase) + { + // The user wrote something all upper case, but happened to match the prefix of the word. For + // example, matching `CR` against both `Create` (a case insensitive prefix match) and `CreateRange` + // (a camel hump match). We want to prefer the latter in this case as the all upper case string + // is a strong signal they wanted camel hump matching here. + PopulateCandidateHumps(); + + // Note: ensure that we match here case sensitively as well. We only want to take this if their + // camel humps actually matched real word starts. + var match = TryCamelCaseMatch(candidate, patternChunk, punctuationStripped, isLowercase: false, candidateHumps); + if (match is { IsCaseSensitive: true }) + return match; + + // Deliberately fall through. + } + // Lengths were the same, this is either a case insensitive or sensitive prefix match. return new PatternMatch( - PatternMatchKind.Prefix, punctuationStripped, isCaseSensitive: _compareInfo.IsPrefix(candidate, patternChunk.Text), - matchedSpan: GetMatchedSpan(0, patternChunk.Text.Length)); + PatternMatchKind.Prefix, punctuationStripped, isCaseSensitive, matchedSpan: GetMatchedSpan(0, patternChunk.Text.Length)); } } - using var candidateHumps = TemporaryArray.Empty; - var patternIsLowercase = patternChunk.IsLowercase; if (caseInsensitiveIndex > 0) { @@ -221,7 +249,8 @@ private static bool ContainsUpperCaseLetter(string pattern) // Now do the more expensive check to see if we're at the start of a word. This is to catch // word matches like CombineBinary. We want to find the hit against '[|Bin|]ary' not // 'Com[|bin|]e' - StringBreaker.AddWordParts(candidate, ref candidateHumps.AsRef()); + PopulateCandidateHumps(); + for (int i = 0, n = candidateHumps.Count; i < n; i++) { var hump = TextSpan.FromBounds(candidateHumps[i].Start, candidateLength); @@ -235,16 +264,17 @@ private static bool ContainsUpperCaseLetter(string pattern) } } - // Didn't have an exact/prefix match, or a high enough quality substring match. - // See if we can find a camel case match. - if (candidateHumps.Count == 0) - StringBreaker.AddWordParts(candidate, ref candidateHumps.AsRef()); + { + // Didn't have an exact/prefix match, or a high enough quality substring match. + // See if we can find a camel case match. + PopulateCandidateHumps(); - // Didn't have an exact/prefix match, or a high enough quality substring match. - // See if we can find a camel case match. - var match = TryCamelCaseMatch(candidate, patternChunk, punctuationStripped, patternIsLowercase, candidateHumps); - if (match != null) - return match; + // Didn't have an exact/prefix match, or a high enough quality substring match. + // See if we can find a camel case match. + var match = TryCamelCaseMatch(candidate, patternChunk, punctuationStripped, patternIsLowercase, candidateHumps); + if (match != null) + return match; + } // If pattern was all lowercase, we allow it to match an all lowercase section of the candidate. But // only after we've tried all other forms first. This is the weakest of all matches. For example, if @@ -265,6 +295,12 @@ private static bool ContainsUpperCaseLetter(string pattern) } return null; + + void PopulateCandidateHumps() + { + if (candidateHumps.Count == 0) + StringBreaker.AddWordParts(candidate, ref candidateHumps.AsRef()); + } } private TextSpan? GetMatchedSpan(int start, int length) diff --git a/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs b/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs index e7ab0bf835488..20b22fff40bf7 100644 --- a/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs +++ b/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs @@ -171,6 +171,12 @@ internal static TokenRenameInfo GetTokenRenameInfo( return TokenRenameInfo.CreateMemberGroupTokenInfo(symbolInfo.CandidateSymbols); } + // If we have overload resolution issues at the callsite, we generally don't want to rename (as it's unclear + // which overload the user is actually calling). However, if there is just a single overload, there's no real + // issue since it's clear which one the user wants to rename in that case. + if (symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure && symbolInfo.CandidateSymbols.Length == 1) + return TokenRenameInfo.CreateMemberGroupTokenInfo(symbolInfo.CandidateSymbols); + if (RenameLocation.ShouldRename(symbolInfo.CandidateReason) && symbolInfo.CandidateSymbols.Length == 1) { diff --git a/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs b/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs index 06e2b900e3035..b3f854ae1a892 100644 --- a/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs +++ b/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs @@ -57,26 +57,31 @@ internal sealed class SerializableSourceText public readonly Checksum ContentChecksum; public SerializableSourceText(TemporaryStorageTextHandle storageHandle) - : this(storageHandle, text: null, storageHandle.ContentHash) + : this(storageHandle, text: null, Checksum.Create(storageHandle.ContentHash)) { } public SerializableSourceText(SourceText text, ImmutableArray contentHash) - : this(storageHandle: null, text, contentHash) + : this(storageHandle: null, text, Checksum.Create(contentHash)) { } - private SerializableSourceText(TemporaryStorageTextHandle? storageHandle, SourceText? text, ImmutableArray contentHash) + public SerializableSourceText(SourceText text, Checksum contentChecksum) + : this(storageHandle: null, text, contentChecksum) + { + } + + private SerializableSourceText(TemporaryStorageTextHandle? storageHandle, SourceText? text, Checksum contentChecksum) { Debug.Assert(storageHandle is null != text is null); _storageHandle = storageHandle; _text = text; - ContentChecksum = Checksum.Create(contentHash); + ContentChecksum = contentChecksum; #if DEBUG var computedContentHash = TryGetText()?.GetContentHash() ?? _storageHandle!.ContentHash; - Debug.Assert(contentHash.SequenceEqual(computedContentHash)); + Debug.Assert(contentChecksum == Checksum.Create(computedContentHash)); #endif } diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs index 0866fcf4877c4..3575763202bbc 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.Host; @@ -86,38 +87,46 @@ public static TokenSemanticInfo GetSemanticInfo( ITypeSymbol? type = null; ITypeSymbol? convertedType = null; ISymbol? declaredSymbol = null; + IPreprocessingSymbol? preprocessingSymbol = null; var allSymbols = ImmutableArray.Empty; + var tokenParent = token.Parent; if (token.RawKind == syntaxKinds.UsingKeyword && - (token.Parent?.RawKind == syntaxKinds.UsingStatement || token.Parent?.RawKind == syntaxKinds.LocalDeclarationStatement)) + (tokenParent?.RawKind == syntaxKinds.UsingStatement || tokenParent?.RawKind == syntaxKinds.LocalDeclarationStatement)) { var usingStatement = token.Parent; - declaredSymbol = semanticFacts.TryGetDisposeMethod(semanticModel, token.Parent, cancellationToken); + declaredSymbol = semanticFacts.TryGetDisposeMethod(semanticModel, tokenParent, cancellationToken); } else if (overriddingIdentifier.HasValue) { // on an "override" token, we'll find the overridden symbol var overriddingSymbol = semanticFacts.GetDeclaredSymbol(semanticModel, overriddingIdentifier.Value, cancellationToken); - var overriddenSymbol = overriddingSymbol.GetOverriddenMember(); + var overriddenSymbol = overriddingSymbol.GetOverriddenMember(allowLooseMatch: true); allSymbols = overriddenSymbol is null ? [] : [overriddenSymbol]; } else { - aliasSymbol = semanticModel.GetAliasInfo(token.Parent!, cancellationToken); + Debug.Assert(tokenParent is not null); + aliasSymbol = semanticModel.GetAliasInfo(tokenParent, cancellationToken); var bindableParent = syntaxFacts.TryGetBindableParent(token); var typeInfo = bindableParent != null ? semanticModel.GetTypeInfo(bindableParent, cancellationToken) : default; type = typeInfo.Type; convertedType = typeInfo.ConvertedType; declaredSymbol = MapSymbol(semanticFacts.GetDeclaredSymbol(semanticModel, token, cancellationToken), type); + preprocessingSymbol = semanticFacts.GetPreprocessingSymbol(semanticModel, tokenParent); - var skipSymbolInfoLookup = declaredSymbol.IsKind(SymbolKind.RangeVariable); - allSymbols = skipSymbolInfoLookup - ? [] - : semanticFacts + if (preprocessingSymbol != null) + { + allSymbols = [preprocessingSymbol]; + } + else if (!declaredSymbol.IsKind(SymbolKind.RangeVariable)) + { + allSymbols = semanticFacts .GetBestOrAllSymbols(semanticModel, bindableParent, token, cancellationToken) .WhereAsArray(s => s != null && !s.Equals(declaredSymbol)) .SelectAsArray(s => MapSymbol(s, type)); + } } // NOTE(cyrusn): This is a workaround to how the semantic model binds and returns @@ -150,6 +159,6 @@ public static TokenSemanticInfo GetSemanticInfo( convertedType = null; } - return new TokenSemanticInfo(declaredSymbol, aliasSymbol, allSymbols, type, convertedType, token.Span); + return new TokenSemanticInfo(declaredSymbol, preprocessingSymbol, aliasSymbol, allSymbols, type, convertedType, token.Span); } } diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/TokenSemanticInfo.cs b/src/Workspaces/Core/Portable/Shared/Extensions/TokenSemanticInfo.cs index 223e26c2860ee..554583066d42a 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/TokenSemanticInfo.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/TokenSemanticInfo.cs @@ -13,6 +13,7 @@ namespace Microsoft.CodeAnalysis.Shared.Extensions; internal readonly struct TokenSemanticInfo( ISymbol declaredSymbol, + IPreprocessingSymbol preprocessingSymbol, IAliasSymbol aliasSymbol, ImmutableArray referencedSymbols, ITypeSymbol type, @@ -20,9 +21,10 @@ internal readonly struct TokenSemanticInfo( TextSpan span) { public static readonly TokenSemanticInfo Empty = new( - null, null, [], null, null, default); + null, null, null, [], null, null, default); public readonly ISymbol DeclaredSymbol = declaredSymbol; + public readonly IPreprocessingSymbol PreprocessingSymbol = preprocessingSymbol; public readonly IAliasSymbol AliasSymbol = aliasSymbol; public readonly ImmutableArray ReferencedSymbols = referencedSymbols; public readonly ITypeSymbol Type = type; @@ -33,6 +35,7 @@ public ImmutableArray GetSymbols(bool includeType) { var result = ArrayBuilder.GetInstance(); result.AddIfNotNull(DeclaredSymbol); + result.AddIfNotNull(PreprocessingSymbol); result.AddIfNotNull(AliasSymbol); result.AddRange(ReferencedSymbols); diff --git a/src/Workspaces/Core/Portable/Utilities/SegmentedListPool.cs b/src/Workspaces/Core/Portable/Utilities/SegmentedListPool.cs index 08225e1b37496..d7def7e7395ce 100644 --- a/src/Workspaces/Core/Portable/Utilities/SegmentedListPool.cs +++ b/src/Workspaces/Core/Portable/Utilities/SegmentedListPool.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; using Microsoft.CodeAnalysis.Collections; namespace Microsoft.CodeAnalysis.Utilities; @@ -32,37 +30,4 @@ internal static PooledObject> GetPooledList(out SegmentedLis classifiedSpans = pooledObject.Object; return pooledObject; } - - /// - /// Computes a list of results based on a provided callback. The callback is passed - /// a to add results to, and additional args to assist the process. If no items - /// are added to the list, then the singleton will be returned. Otherwise the - /// instance will be returned. - /// - public static IReadOnlyList ComputeList( - Action> addItems, - TArgs args, - // Only used to allow type inference to work at callsite - T? _) - { - var pooledObject = GetPooledList(out var list); - - addItems(args, list); - - // If the result was empty, return it to the pool, and just pass back the empty array singleton. - if (pooledObject.Object.Count == 0) - { - pooledObject.Dispose(); - return []; - } - - // Otherwise, do not dispose. Caller needs this value to stay alive. - return list; - } -} - -internal static class SegmentedListPool -{ - public static IReadOnlyList ComputeList(Action> addItems, TArgs args) - => SegmentedListPool.ComputeList(addItems, args, _: default); } diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/FileWatchedPortableExecutableReferenceFactory.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/FileWatchedPortableExecutableReferenceFactory.cs index 3cc09158ef1e1..66880110e594b 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/FileWatchedPortableExecutableReferenceFactory.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/FileWatchedPortableExecutableReferenceFactory.cs @@ -109,7 +109,7 @@ static ImmutableArray GetAdditionalWatchedDirectories() referenceDirectories.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages")); } - return referenceDirectories.SelectAsArray(static d => new WatchedDirectory(d, ".dll")); + return referenceDirectories.SelectAsArray(static d => new WatchedDirectory(d, [".dll"])); } } diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IFileChangeWatcher.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IFileChangeWatcher.cs index 0e7a9612cef12..67458f3e7cdeb 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IFileChangeWatcher.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IFileChangeWatcher.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Immutable; +using System.Linq; namespace Microsoft.CodeAnalysis.ProjectSystem; @@ -26,7 +27,7 @@ internal interface IFileChangeWatcher /// internal sealed class WatchedDirectory { - public WatchedDirectory(string path, string? extensionFilter) + public WatchedDirectory(string path, ImmutableArray extensionFilters) { // We are doing string comparisons with this path, so ensure it has a trailing directory separator so we don't get confused with sibling // paths that won't actually be covered. For example, if we're watching C:\Directory we wouldn't see changes to C:\DirectorySibling\Foo.txt. @@ -35,13 +36,13 @@ public WatchedDirectory(string path, string? extensionFilter) path += System.IO.Path.DirectorySeparatorChar; } - if (extensionFilter != null && !extensionFilter.StartsWith(".")) + if (extensionFilters.Any(static filter => !filter.StartsWith("."))) { - throw new ArgumentException($"{nameof(extensionFilter)} should start with a period.", nameof(extensionFilter)); + throw new ArgumentException($"{nameof(extensionFilters)} should only contain entries starting with a period.", nameof(extensionFilters)); } Path = path; - ExtensionFilter = extensionFilter; + ExtensionFilters = extensionFilters; } public string Path { get; } @@ -49,7 +50,7 @@ public WatchedDirectory(string path, string? extensionFilter) /// /// If non-null, only watch the directory for changes to a specific extension. String always starts with a period. /// - public string? ExtensionFilter { get; } + public ImmutableArray ExtensionFilters { get; } public static bool FilePathCoveredByWatchedDirectories(ImmutableArray watchedDirectories, string filePath, StringComparison stringComparison) { @@ -57,10 +58,10 @@ public static bool FilePathCoveredByWatchedDirectories(ImmutableArray filePath.EndsWith(filter, stringComparison))) { return true; } diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs index 3eb2c57e75cd6..5fcbed2ccaeb0 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProject.cs @@ -222,8 +222,8 @@ static ImmutableArray GetWatchedDirectories(string? language, return language switch { - LanguageNames.VisualBasic => [new(rootPath, ".vb")], - LanguageNames.CSharp => [new(rootPath, ".cs"), new(rootPath, ".razor"), new(rootPath, ".cshtml")], + LanguageNames.VisualBasic => [new(rootPath, [".vb"])], + LanguageNames.CSharp => [new(rootPath, [".cs", ".razor", ".cshtml"])], _ => [] }; } diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.cs index 128db0e32d8bf..eb3f82820973e 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.cs @@ -556,22 +556,23 @@ private static ProjectUpdateState ConvertMetadataReferencesToProjectReferences_N // PERF: call GetRequiredProjectState instead of GetRequiredProject, otherwise creating a new project // might force all Project instances to get created. var projectState = solutionChanges.Solution.GetRequiredProjectState(projectIdToRetarget); - foreach (var reference in projectState.MetadataReferences.OfType()) + foreach (var reference in projectState.MetadataReferences) { - if (string.Equals(reference.FilePath, outputPath, StringComparison.OrdinalIgnoreCase)) + if (reference is PortableExecutableReference peReference + && string.Equals(peReference.FilePath, outputPath, StringComparison.OrdinalIgnoreCase)) { - projectUpdateState = projectUpdateState.WithIncrementalMetadataReferenceRemoved(reference); + projectUpdateState = projectUpdateState.WithIncrementalMetadataReferenceRemoved(peReference); - var projectReference = new ProjectReference(projectIdToReference, reference.Properties.Aliases, reference.Properties.EmbedInteropTypes); + var projectReference = new ProjectReference(projectIdToReference, peReference.Properties.Aliases, peReference.Properties.EmbedInteropTypes); var newSolution = solutionChanges.Solution - .RemoveMetadataReference(projectIdToRetarget, reference) + .RemoveMetadataReference(projectIdToRetarget, peReference) .AddProjectReference(projectIdToRetarget, projectReference); solutionChanges.UpdateSolutionForProjectAction(projectIdToRetarget, newSolution); projectUpdateState = GetReferenceInformation(projectIdToRetarget, projectUpdateState, out var projectInfo); projectUpdateState = projectUpdateState.WithProjectReferenceInfo(projectIdToRetarget, - projectInfo.WithConvertedProjectReference(reference.FilePath!, projectReference)); + projectInfo.WithConvertedProjectReference(peReference.FilePath!, projectReference)); // We have converted one, but you could have more than one reference with different aliases that // we need to convert, so we'll keep going diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs index 2287ce0a9f98a..63bd72d51d552 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs @@ -846,7 +846,7 @@ public ProjectState WithMetadataReferences(IReadOnlyList meta return With(projectInfo: ProjectInfo.With(metadataReferences: metadataReferences).WithVersion(Version.GetNewerVersion())); } - public ProjectState WithAnalyzerReferences(IEnumerable analyzerReferences) + public ProjectState WithAnalyzerReferences(IReadOnlyList analyzerReferences) { if (analyzerReferences == AnalyzerReferences) { diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs index e4a6a3daad0b5..ecb35bc29f284 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs @@ -132,7 +132,8 @@ public Workspace Workspace /// /// True if the solution contains a project with the specified project ID. /// - public bool ContainsProject([NotNullWhen(returnValue: true)] ProjectId? projectId) => this.SolutionState.ContainsProject(projectId); + public bool ContainsProject([NotNullWhen(returnValue: true)] ProjectId? projectId) + => this.SolutionState.ContainsProject(projectId); /// /// Gets the project in this solution with the specified project ID. @@ -788,9 +789,7 @@ public Solution AddAnalyzerReferences(ProjectId projectId, IEnumerable @@ -826,7 +829,14 @@ public Solution RemoveAnalyzerReference(ProjectId projectId, AnalyzerReference a if (!oldProject.AnalyzerReferences.Contains(analyzerReference)) throw new InvalidOperationException(WorkspacesResources.Project_does_not_contain_specified_reference); - return WithCompilationState(CompilationState.RemoveAnalyzerReference(projectId, analyzerReference)); + var builder = new FixedSizeArrayBuilder(oldProject.AnalyzerReferences.Count - 1); + foreach (var reference in oldProject.AnalyzerReferences) + { + if (!reference.Equals(analyzerReference)) + builder.Add(reference); + } + + return WithCompilationState(CompilationState.WithProjectAnalyzerReferences(projectId, builder.MoveToImmutable())); } /// diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index b8af0014c4c18..7a638eda52f0e 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -168,8 +168,8 @@ private SolutionCompilationState ForkProject( } /// - /// Same as except that it will still fork even if newSolutionState is unchanged from . + /// Same as + /// except that it will still fork even if newSolutionState is unchanged from . /// private SolutionCompilationState ForceForkProject( StateChange stateChange, @@ -713,17 +713,6 @@ public SolutionCompilationState WithProjectMetadataReferences( forkTracker: true); } - /// - public SolutionCompilationState AddAnalyzerReferences(StateChange stateChange, ImmutableArray analyzerReferences) - { - return ForkProject( - stateChange, - static (stateChange, analyzerReferences) => new TranslationAction.AddOrRemoveAnalyzerReferencesAction( - stateChange.OldProjectState, stateChange.NewProjectState, referencesToAdd: analyzerReferences), - forkTracker: true, - arg: analyzerReferences); - } - public SolutionCompilationState AddAnalyzerReferences(IReadOnlyCollection analyzerReferences) { // Note: This is the codepath for adding analyzers from vsixes. Importantly, we do not ever get SGs added from @@ -748,17 +737,6 @@ public SolutionCompilationState WithAnalyzerReferences(IReadOnlyList - public SolutionCompilationState RemoveAnalyzerReference(ProjectId projectId, AnalyzerReference analyzerReference) - { - return ForkProject( - this.SolutionState.RemoveAnalyzerReference(projectId, analyzerReference), - static (stateChange, analyzerReference) => new TranslationAction.AddOrRemoveAnalyzerReferencesAction( - stateChange.OldProjectState, stateChange.NewProjectState, referencesToRemove: [analyzerReference]), - forkTracker: true, - arg: analyzerReference); - } - /// public SolutionCompilationState WithProjectAnalyzerReferences( ProjectId projectId, IReadOnlyList analyzerReferences) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index bc26c954a1a28..51a3352bd9452 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -879,41 +879,6 @@ public StateChange WithProjectMetadataReferences(ProjectId projectId, IReadOnlyL return ForkProject(oldProject, newProject); } - /// - /// Create a new solution instance with the project specified updated to include the - /// specified analyzer references. - /// - public StateChange AddAnalyzerReferences(ProjectId projectId, ImmutableArray analyzerReferences) - { - var oldProject = GetRequiredProjectState(projectId); - if (analyzerReferences.Length == 0) - { - return new(this, oldProject, oldProject); - } - - var oldReferences = oldProject.AnalyzerReferences.ToImmutableArray(); - var newReferences = oldReferences.AddRange(analyzerReferences); - - return ForkProject(oldProject, oldProject.WithAnalyzerReferences(newReferences)); - } - - /// - /// Create a new solution instance with the project specified updated to no longer include - /// the specified analyzer reference. - /// - public StateChange RemoveAnalyzerReference(ProjectId projectId, AnalyzerReference analyzerReference) - { - var oldProject = GetRequiredProjectState(projectId); - var oldReferences = oldProject.AnalyzerReferences.ToImmutableArray(); - var newReferences = oldReferences.Remove(analyzerReference); - if (oldReferences == newReferences) - { - return new(this, oldProject, oldProject); - } - - return ForkProject(oldProject, oldProject.WithAnalyzerReferences(newReferences)); - } - /// /// Create a new solution instance with the project specified updated to include only the /// specified analyzer references. diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs index 32bebdc86d8d5..92c49675d850a 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs @@ -2,58 +2,50 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Immutable; +using Roslyn.Utilities; + namespace Microsoft.CodeAnalysis; public partial class Solution { /// - /// Strongly held reference to the semantic model for the active document. By strongly holding onto it, we ensure - /// that it won't be GC'ed between feature requests from multiple features that care about it. As the active - /// document has the most features running on it continuously, we definitely do not want to drop this. Note: this - /// cached value is only to help with performance. Not with correctness. Importantly, the concept of 'active - /// document' is itself fundamentally racy. That's ok though as we simply want to settle on these semantic models - /// settling into a stable state over time. We don't need to be perfect about it. They are intentionally not - /// locked either as we would only have contention right when switching to a new active document, and we would still - /// latch onto the new document very quickly. + /// Strongly held reference to the semantic models for the active document (and its related documents linked into + /// other projects). By strongly holding onto them, we ensure that they won't be GC'ed between feature requests + /// from multiple features that care about it. As the active document has the most features running on it + /// continuously, we definitely do not want to drop this. Note: this cached value is only to help with performance. + /// Not with correctness. Importantly, the concept of 'active document' is itself fundamentally racy. That's ok + /// though as we simply want to settle on these semantic models settling into a stable state over time. We don't + /// need to be perfect about it. /// - /// - /// It is fine for these fields to never be read. The purpose is simply to keep a strong reference around so that - /// they will not be GC'ed as long as the active document stays the same. - /// -#pragma warning disable IDE0052 // Remove unread private members - private SemanticModel? _activeDocumentSemanticModel; - - /// - private SemanticModel? _activeDocumentNullableDisabledSemanticModel; -#pragma warning restore IDE0052 // Remove unread private members - - internal void OnSemanticModelObtained(DocumentId documentId, SemanticModel semanticModel) + private ImmutableArray<(DocumentId documentId, SemanticModel semanticModel)> _activeSemanticModels = []; + + internal void OnSemanticModelObtained( + DocumentId documentId, SemanticModel semanticModel) { var service = this.Services.GetRequiredService(); - var activeDocumentId = service.TryGetActiveDocument(); - if (activeDocumentId is null) - { - // no active document? then clear out any caches we have. - _activeDocumentSemanticModel = null; - _activeDocumentNullableDisabledSemanticModel = null; - } - else if (activeDocumentId != documentId) - { - // We have an active document, but we just obtained the semantic model for some other doc. Nothing to do - // here, we don't want to cache this. + // Operate on a local reference to the immutable array to ensure a consistent view of it. + var localArray = _activeSemanticModels; + + // No need to do anything if we're already caching this pair. + if (localArray.Contains((documentId, semanticModel))) return; - } - else - { - // Ok. We just obtained the semantic model for the active document. Make a strong reference to it so that - // other features that wake up for this active document are sure to be able to reuse the same one. -#pragma warning disable RSEXPERIMENTAL001 // sym-shipped usage of experimental API - if (semanticModel.NullableAnalysisIsDisabled) - _activeDocumentNullableDisabledSemanticModel = semanticModel; - else - _activeDocumentSemanticModel = semanticModel; -#pragma warning restore RSEXPERIMENTAL001 - } + + var activeDocumentId = service.TryGetActiveDocument(); + var relatedDocumentIds = activeDocumentId is null ? [] : this.GetRelatedDocumentIds(activeDocumentId); + + // Remove any cached values for documents that are no longer the active document. + localArray = localArray.RemoveAll( + tuple => !relatedDocumentIds.Contains(tuple.documentId)); + + // Now cache this doc/semantic model pair if it's in the related document set. + if (relatedDocumentIds.Contains(documentId)) + localArray = localArray.Add((documentId, semanticModel)); + + // Note: this code is racy. We could have two threads executing the code above, while only one thread will win + // here. We accept that as this code is just intended to help just by making some strong references to semantic + // models to prevent them from being GC'ed. We don't need to be perfect about it. + _activeSemanticModels = localArray; } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs index 33ead1daf977d..1e5f9513360dc 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs @@ -138,9 +138,18 @@ public ImmutableArray SelectAsArray(Func AddRange(ImmutableArray states) - => new(_ids.AddRange(states.Select(state => state.Id)), - States.AddRange(states.Select(state => KeyValuePairUtil.Create(state.Id, state))), - filePathToDocumentIds: null); + { + using var pooledIds = SharedPools.Default>().GetPooledObject(); + var ids = pooledIds.Object; + + foreach (var state in states) + ids.Add(state.Id); + + return new( + _ids.AddRange(ids), + States.AddRange(states.Select(state => KeyValuePairUtil.Create(state.Id, state))), + filePathToDocumentIds: null); + } public TextDocumentStates RemoveRange(ImmutableArray ids) { diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs index a53b16e4e9e24..2de8d674b6024 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs @@ -57,6 +57,15 @@ await this.SetCurrentSolutionAsync( return; + // + // Given the current state of a , produced an updated version of the source generator + // execution map based on the changes in . Each item in signifies a particular project (if projectId is non-null) or the solution as a + // whole (if it is null). The forceRegeneration signifies if generators should be rerun even if the + // contents of the solution are the same. If a project is specified in then both + // it and all dependent projects of it will have their source generator versions updated. If the solution is + // specified, then all projects will have their versions updated. + // static SourceGeneratorExecutionVersionMap GetUpdatedSourceGeneratorVersions( Solution solution, ImmutableSegmentedList<(ProjectId? projectId, bool forceRegeneration)> projectIds) { diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs index 0ab0a08695b10..de80e417b1cae 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Immutable; +using System.Composition; using System.IO; using System.Linq; using System.Reflection; @@ -12,8 +13,12 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; @@ -985,15 +990,39 @@ public Assembly LoadFromPath(string fullPath) => throw new InvalidOperationException("These tests should not be loading analyzer assemblies in those host workspace, only in the remote one."); } - [Fact] - public async Task UpdatingAnalyzerReferenceReloadsGenerators() + [PartNotDiscoverable] + [ExportWorkspaceService(typeof(IWorkspaceConfigurationService), ServiceLayer.Test), System.Composition.Shared] + [method: ImportingConstructor] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + private sealed class TestWorkspaceConfigurationService(IGlobalOptionService globalOptionService) : IWorkspaceConfigurationService + { + public WorkspaceConfigurationOptions Options => globalOptionService.GetWorkspaceConfigurationOptions(); + } + + [Theory, CombinatorialData] + internal async Task UpdatingAnalyzerReferenceReloadsGenerators( + SourceGeneratorExecutionPreference executionPreference) { // We have two versions of the same source generator attached to this project as a resource. Each creates a // 'HelloWorld' class, just with a different string it emits inside. const string AnalyzerResourceV1 = @"Microsoft.CodeAnalysis.UnitTests.Resources.Microsoft.CodeAnalysis.TestAnalyzerReference.dll.v1"; const string AnalyzerResourceV2 = @"Microsoft.CodeAnalysis.UnitTests.Resources.Microsoft.CodeAnalysis.TestAnalyzerReference.dll.v2"; - using var workspace = CreateWorkspace(testHost: TestHost.OutOfProcess); + using var workspace = CreateWorkspace([typeof(TestWorkspaceConfigurationService)], TestHost.OutOfProcess); + + // Ensure the local and remote sides agree on how we're executing source generators. + var mefServices = (VisualStudioMefHostServices)workspace.Services.HostServices; + var globalOptionService = mefServices.GetExportedValue(); + globalOptionService.SetGlobalOption(WorkspaceConfigurationOptionsStorage.SourceGeneratorExecution, executionPreference); + + using var client = await InProcRemoteHostClient.GetTestClientAsync(workspace).ConfigureAwait(false); + + var workspaceConfigurationService = workspace.Services.GetRequiredService(); + + var remoteProcessId = await client.TryInvokeAsync( + (service, cancellationToken) => service.InitializeAsync(workspaceConfigurationService.Options with { SourceGeneratorExecution = executionPreference }, cancellationToken), + CancellationToken.None).ConfigureAwait(false); + var solution = workspace.CurrentSolution; var project1 = solution.AddProject("P1", "P1", LanguageNames.CSharp); @@ -1036,6 +1065,19 @@ public async Task UpdatingAnalyzerReferenceReloadsGenerators() // syncing this new reference to the oop side, which will load the analyzer reference in a dedicated ALC. project1 = project1.WithAnalyzerReferences([new AnalyzerFileReference(analyzerPath, DoNotLoadAssemblyLoader.Instance)]); + // In balanced mode, emulate the project system notifying about the updated reference on disk, which will + // cause us to update source generators versions. + if (executionPreference is SourceGeneratorExecutionPreference.Balanced) + { + Assert.True(workspace.TryApplyChanges(project1.Solution)); + workspace.EnqueueUpdateSourceGeneratorVersion(project1.Id, forceRegeneration: true); + + var waiter = (IAsynchronousOperationWaiter)mefServices.GetExportedValue().GetListener(FeatureAttribute.SourceGenerators); + await waiter.ExpeditedWaitAsync(); + + project1 = workspace.CurrentSolution.GetRequiredProject(project1.Id); + } + var generatedDocuments = await project1.GetSourceGeneratedDocumentsAsync(); var helloWorldDoc = generatedDocuments.Single(d => d.Name == "HelloWorld.cs"); diff --git a/src/Workspaces/MSBuildTest/Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj b/src/Workspaces/MSBuildTest/Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj index 9c4abc25d359d..fb8224323dc55 100644 --- a/src/Workspaces/MSBuildTest/Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj +++ b/src/Workspaces/MSBuildTest/Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj @@ -4,7 +4,7 @@ Library Microsoft.CodeAnalysis.MSBuild.UnitTests - $(NetVS);net472 + $(NetRoslyn);net472 diff --git a/src/Workspaces/Remote/Core/BrokeredServiceDescriptors.cs b/src/Workspaces/Remote/Core/BrokeredServiceDescriptors.cs index 4e5dfd34a0838..430bdbf39b6da 100644 --- a/src/Workspaces/Remote/Core/BrokeredServiceDescriptors.cs +++ b/src/Workspaces/Remote/Core/BrokeredServiceDescriptors.cs @@ -81,6 +81,8 @@ protected override JsonRpcConnection CreateConnection(JsonRpc jsonRpc) CreateDebuggerServiceDescriptor("SymbolLocatorService", new Version(0, 1), new MultiplexingStream.Options { ProtocolMajorVersion = 3 }); public static readonly ServiceRpcDescriptor DebuggerSourceLinkService = CreateDebuggerServiceDescriptor("SourceLinkService", new Version(0, 1), new MultiplexingStream.Options { ProtocolMajorVersion = 3 }); + public static readonly ServiceRpcDescriptor ProjectSystemQueryExecutionService = + CreateDescriptor(new ServiceMoniker("Microsoft.VisualStudio.ProjectSystem.Query.Remoting.QueryExecutionService", new Version(0, 2))); public static ServiceMoniker CreateMoniker(string namespaceName, string componentName, string serviceName, Version? version) => new(namespaceName + "." + componentName + "." + serviceName, version); diff --git a/src/Workspaces/Remote/Core/EditAndContinue/ManagedHotReloadLanguageService.cs b/src/Workspaces/Remote/Core/EditAndContinue/ManagedHotReloadLanguageService.cs index b3bc1cd42fbe9..2e7d6487c75d7 100644 --- a/src/Workspaces/Remote/Core/EditAndContinue/ManagedHotReloadLanguageService.cs +++ b/src/Workspaces/Remote/Core/EditAndContinue/ManagedHotReloadLanguageService.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Immutable; using System.ComponentModel.Composition; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.BrokeredServices; @@ -13,18 +14,20 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.EditAndContinue; [Export(typeof(IManagedHotReloadLanguageService))] +[Export(typeof(IManagedHotReloadLanguageService2))] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed partial class ManagedHotReloadLanguageService( IServiceBrokerProvider serviceBrokerProvider, IEditAndContinueService encService, - SolutionSnapshotRegistry solutionSnapshotRegistry) : IManagedHotReloadLanguageService + SolutionSnapshotRegistry solutionSnapshotRegistry) : IManagedHotReloadLanguageService2 { private sealed class PdbMatchingSourceTextProvider : IPdbMatchingSourceTextProvider { @@ -152,6 +155,35 @@ public ValueTask CommitUpdatesAsync(CancellationToken cancellationToken) return ValueTaskFactory.CompletedTask; } + public async ValueTask UpdateBaselinesAsync(ImmutableArray projectPaths, CancellationToken cancellationToken) + { + if (_disabled) + { + return; + } + + try + { + Contract.ThrowIfNull(_debuggingSession); + + var currentDesignTimeSolution = await GetCurrentDesignTimeSolutionAsync(cancellationToken).ConfigureAwait(false); + var currentCompileTimeSolution = GetCurrentCompileTimeSolution(currentDesignTimeSolution); + + _committedDesignTimeSolution = currentDesignTimeSolution; + + var projectIds = from path in projectPaths + let projectId = currentCompileTimeSolution.Projects.FirstOrDefault(project => project.FilePath == path)?.Id + where projectId != null + select projectId; + + encService.UpdateBaselines(_debuggingSession.Value, currentCompileTimeSolution, projectIds.ToImmutableArray()); + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) + { + Disable(); + } + } + public ValueTask DiscardUpdatesAsync(CancellationToken cancellationToken) { if (_disabled) @@ -236,7 +268,10 @@ public async ValueTask HasChangesAsync(string? sourceFilePath, Cancellatio } } - public async ValueTask GetUpdatesAsync(CancellationToken cancellationToken) + public ValueTask GetUpdatesAsync(CancellationToken cancellationToken) + => GetUpdatesAsync(runningProjects: [], cancellationToken); + + public async ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken) { if (_disabled) { @@ -254,7 +289,11 @@ public async ValueTask GetUpdatesAsync(CancellationToke try { - results = (await encService.EmitSolutionUpdateAsync(_debuggingSession.Value, solution, s_emptyActiveStatementProvider, cancellationToken).ConfigureAwait(false)).Dehydrate(); + using var _ = PooledHashSet.GetInstance(out var runningProjectPaths); + runningProjectPaths.AddAll(runningProjects); + var runningProjectIds = solution.Projects.Where(p => p.FilePath != null && runningProjectPaths.Contains(p.FilePath)).Select(static p => p.Id).ToImmutableHashSet(); + + results = (await encService.EmitSolutionUpdateAsync(_debuggingSession.Value, solution, runningProjectIds, s_emptyActiveStatementProvider, cancellationToken).ConfigureAwait(false)).Dehydrate(); } catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) { @@ -270,7 +309,9 @@ public async ValueTask GetUpdatesAsync(CancellationToke Diagnostics = [DiagnosticData.Create(designTimeSolution, diagnostic, project: null)], RudeEdits = [], ModuleUpdates = new ModuleUpdates(ModuleUpdateStatus.RestartRequired, []), - SyntaxError = null + SyntaxError = null, + ProjectsToRebuild = [], + ProjectsToRestart = [], }; } diff --git a/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs b/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs index 916e9357792d8..079bc05f93d52 100644 --- a/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs +++ b/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs @@ -21,11 +21,13 @@ internal interface IRemoteAssetSynchronizationService /// Synchronize the text changes made by a user to a particular document as they are editing it. By sending over /// the text changes as they happen, we can attempt to 'prime' the remote asset cache with a final that is built based off of retrieving the remote source text with a checksum corresponding - /// to and then applying the to it. Then, when - /// the next remote call comes in for the new solution snapshot, it can hopefully just pluck that text out of the - /// cache without having to sync the entire contents of the file over. + /// to baseTextChecksum and then applying the textChanges to it. Then, when the next remote call comes in for the + /// new solution snapshot, it can hopefully just pluck that text out of the cache without having to sync the + /// entire contents of the file over. /// - ValueTask SynchronizeTextAsync(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges, CancellationToken cancellationToken); + ValueTask SynchronizeTextChangesAsync( + ImmutableArray<(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges, Checksum newTextChecksum)> changes, + CancellationToken cancellationToken); /// /// Synchronize over what the user's current active document is that they're editing. This can then be used by the diff --git a/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs b/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs index c86c7482b169d..f6cc7c147aab3 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs @@ -48,30 +48,35 @@ public ValueTask SynchronizeActiveDocumentAsync(DocumentId? documentId, Cancella return ValueTaskFactory.CompletedTask; } - public ValueTask SynchronizeTextAsync(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges, CancellationToken cancellationToken) + public ValueTask SynchronizeTextChangesAsync( + ImmutableArray<(DocumentId documentId, Checksum baseTextChecksum, ImmutableArray textChanges, Checksum newTextChecksum)> changes, + CancellationToken cancellationToken) { return RunServiceAsync(async cancellationToken => { var workspace = GetWorkspace(); - using (RoslynLogger.LogBlock(FunctionId.RemoteHostService_SynchronizeTextAsync, Checksum.GetChecksumLogInfo, baseTextChecksum, cancellationToken)) + using (RoslynLogger.LogBlock(FunctionId.RemoteHostService_SynchronizeTextAsync, cancellationToken)) { - // Try to get the text associated with baseTextChecksum - var text = await TryGetSourceTextAsync(WorkspaceManager, workspace, documentId, baseTextChecksum, cancellationToken).ConfigureAwait(false); - if (text == null) + foreach (var (documentId, baseTextChecksum, textChanges, newTextChecksum) in changes) { - // it won't bring in base text if it is not there already. - // text needed will be pulled in when there is request - return; - } + // Try to get the text associated with baseTextChecksum + var text = await TryGetSourceTextAsync(WorkspaceManager, workspace, documentId, baseTextChecksum, cancellationToken).ConfigureAwait(false); + if (text == null) + { + // it won't bring in base text if it is not there already. + // text needed will be pulled in when there is request + continue; + } - // Now attempt to manually apply the edit, producing the new forked text. Store that directly in - // the asset cache so that future calls to retrieve it can do so quickly, without synchronizing over - // the entire document. - var newText = text.WithChanges(textChanges); - var newSerializableText = new SerializableSourceText(newText, newText.GetContentHash()); + // Now attempt to manually apply the edit, producing the new forked text. Store that directly in + // the asset cache so that future calls to retrieve it can do so quickly, without synchronizing over + // the entire document. + var newText = text.WithChanges(textChanges); + var newSerializableText = new SerializableSourceText(newText, newTextChecksum); - WorkspaceManager.SolutionAssetCache.GetOrAdd(newSerializableText.ContentChecksum, newSerializableText); + WorkspaceManager.SolutionAssetCache.GetOrAdd(newSerializableText.ContentChecksum, newSerializableText); + } } return; diff --git a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs index 1f57c7803b213..d53a007a69e39 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs @@ -141,7 +141,7 @@ public ValueTask> GetDocumentDiagnosticsAsync(Che /// Remote API. /// public ValueTask EmitSolutionUpdateAsync( - Checksum solutionChecksum, RemoteServiceCallbackId callbackId, DebuggingSessionId sessionId, CancellationToken cancellationToken) + Checksum solutionChecksum, RemoteServiceCallbackId callbackId, DebuggingSessionId sessionId, IImmutableSet runningProjects, CancellationToken cancellationToken) { return RunServiceAsync(solutionChecksum, async solution => { @@ -149,7 +149,7 @@ public ValueTask> GetDocumentDiagnosticsAsync(Che try { - return (await service.EmitSolutionUpdateAsync(sessionId, solution, CreateActiveStatementSpanProvider(callbackId), cancellationToken).ConfigureAwait(false)).Dehydrate(); + return (await service.EmitSolutionUpdateAsync(sessionId, solution, runningProjects, CreateActiveStatementSpanProvider(callbackId), cancellationToken).ConfigureAwait(false)).Dehydrate(); } catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) { @@ -159,6 +159,8 @@ public ValueTask> GetDocumentDiagnosticsAsync(Che Diagnostics = GetUnexpectedUpdateError(solution, e), RudeEdits = [], SyntaxError = null, + ProjectsToRebuild = [], + ProjectsToRestart = [], }; } }, cancellationToken); @@ -195,6 +197,18 @@ public ValueTask DiscardSolutionUpdateAsync(DebuggingSessionId sessionId, Cancel }, cancellationToken); } + /// + /// Remote API. + /// + public ValueTask UpdateBaselinesAsync(Checksum solutionChecksum, DebuggingSessionId sessionId, ImmutableArray rebuiltProjects, CancellationToken cancellationToken) + { + return RunServiceAsync(solutionChecksum, solution => + { + GetService().UpdateBaselines(sessionId, solution, rebuiltProjects); + return default; + }, cancellationToken); + } + /// /// Remote API. /// diff --git a/src/Workspaces/Remote/ServiceHub/Services/MissingImportDiscovery/RemoteMissingImportDiscoveryService.cs b/src/Workspaces/Remote/ServiceHub/Services/MissingImportDiscovery/RemoteMissingImportDiscoveryService.cs index ce5eefcae2312..8261f14ec5cd5 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/MissingImportDiscovery/RemoteMissingImportDiscoveryService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/MissingImportDiscovery/RemoteMissingImportDiscoveryService.cs @@ -13,7 +13,10 @@ namespace Microsoft.CodeAnalysis.Remote; -internal sealed class RemoteMissingImportDiscoveryService : BrokeredServiceBase, IRemoteMissingImportDiscoveryService +internal sealed class RemoteMissingImportDiscoveryService( + in BrokeredServiceBase.ServiceConstructionArguments arguments, + RemoteCallback callback) + : BrokeredServiceBase(arguments), IRemoteMissingImportDiscoveryService { internal sealed class Factory : FactoryBase { @@ -21,13 +24,7 @@ protected override IRemoteMissingImportDiscoveryService CreateService(in Service => new RemoteMissingImportDiscoveryService(arguments, callback); } - private readonly RemoteCallback _callback; - - public RemoteMissingImportDiscoveryService(in ServiceConstructionArguments arguments, RemoteCallback callback) - : base(arguments) - { - _callback = callback; - } + private readonly RemoteCallback _callback = callback; public ValueTask> GetFixesAsync( Checksum solutionChecksum, @@ -42,7 +39,9 @@ public ValueTask> GetFixesAsync( { return RunServiceAsync(solutionChecksum, async solution => { - var document = solution.GetRequiredDocument(documentId); + var document = solution.GetDocument(documentId); + if (document is null) + return []; var service = document.GetRequiredLanguageService(); @@ -69,7 +68,9 @@ public ValueTask> GetUniqueFixesAsync( { return RunServiceAsync(solutionChecksum, async solution => { - var document = solution.GetRequiredDocument(documentId); + var document = solution.GetDocument(documentId); + if (document is null) + return []; var service = document.GetRequiredLanguageService(); @@ -94,16 +95,13 @@ public ValueTask> GetUniqueFixesAsync( /// /// Ideally we would not need to bounce back to the host for this. /// - private sealed class SymbolSearchService : ISymbolSearchService + private sealed class SymbolSearchService( + RemoteCallback callback, + RemoteServiceCallbackId callbackId) + : ISymbolSearchService { - private readonly RemoteCallback _callback; - private readonly RemoteServiceCallbackId _callbackId; - - public SymbolSearchService(RemoteCallback callback, RemoteServiceCallbackId callbackId) - { - _callback = callback; - _callbackId = callbackId; - } + private readonly RemoteCallback _callback = callback; + private readonly RemoteServiceCallbackId _callbackId = callbackId; public ValueTask> FindPackagesWithTypeAsync(string source, string name, int arity, CancellationToken cancellationToken) => _callback.InvokeAsync((callback, cancellationToken) => callback.FindPackagesWithTypeAsync(_callbackId, source, name, arity, cancellationToken), cancellationToken); diff --git a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.Caching.cs b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.Caching.cs index 07e30f33df3e7..4374242bb0683 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.Caching.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.Caching.cs @@ -86,9 +86,18 @@ private static string GetPersistenceName(ClassificationType type) var classifiedSpans = await TryGetOrReadCachedSemanticClassificationsAsync( documentKey, type, checksum, cancellationToken).ConfigureAwait(false); var textSpanIntervalTree = new TextSpanMutableIntervalTree(textSpans); - return classifiedSpans.IsDefault - ? null - : SerializableClassifiedSpans.Dehydrate(classifiedSpans.WhereAsArray(c => textSpanIntervalTree.HasIntervalThatIntersectsWith(c.TextSpan))); + + if (classifiedSpans.IsDefault) + return null; + + using var _ = Classifier.GetPooledList(out var temp); + foreach (var span in classifiedSpans) + { + if (textSpanIntervalTree.HasIntervalThatIntersectsWith(span.TextSpan)) + temp.Add(span); + } + + return SerializableClassifiedSpans.Dehydrate(temp); } private static async ValueTask CacheClassificationsAsync( diff --git a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs index 26c46f8ed5060..6369abb39128b 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs @@ -57,7 +57,7 @@ await classificationService.AddClassificationsAsync( _workQueue.AddWork((document, type, options)); } - return SerializableClassifiedSpans.Dehydrate([.. temp]); + return SerializableClassifiedSpans.Dehydrate(temp); }, cancellationToken); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeGeneration/CSharpSyntaxTokens.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeGeneration/CSharpSyntaxTokens.cs index 9b920f587a42d..2106a867bae00 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeGeneration/CSharpSyntaxTokens.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeGeneration/CSharpSyntaxTokens.cs @@ -27,6 +27,7 @@ internal static class CSharpSyntaxTokens public static readonly SyntaxToken DotDotToken = Token(SyntaxKind.DotDotToken); public static readonly SyntaxToken DoubleKeyword = Token(SyntaxKind.DoubleKeyword); public static readonly SyntaxToken EndOfDocumentationCommentToken = Token(SyntaxKind.EndOfDocumentationCommentToken); + public static readonly SyntaxToken EqualsToken = Token(SyntaxKind.EqualsToken); public static readonly SyntaxToken ExplicitKeyword = Token(SyntaxKind.ExplicitKeyword); public static readonly SyntaxToken ExternKeyword = Token(SyntaxKind.ExternKeyword); public static readonly SyntaxToken FileKeyword = Token(SyntaxKind.FileKeyword); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs index 358c3dd605363..596029387e652 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs @@ -88,6 +88,10 @@ private static Option2> CreateOption( CodeStyleOptionGroups.ExpressionLevelPreferences, "csharp_style_prefer_utf8_string_literals", defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement); + public static readonly Option2> PreferUnboundGenericTypeInNameOf = CreateOption( + CodeStyleOptionGroups.ExpressionLevelPreferences, "csharp_style_prefer_unbound_generic_type_in_nameof", + defaultValue: CodeStyleOption2.TrueWithSuggestionEnforcement); + public static readonly CodeStyleOption2 NeverWithSilentEnforcement = new(ExpressionBodyPreference.Never, NotificationOption2.Silent); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs index 11f2d9ceff053..991dbfe11d3c8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs @@ -23,6 +23,8 @@ internal class CSharpVirtualCharService : AbstractVirtualCharService { public static readonly IVirtualCharService Instance = new CSharpVirtualCharService(); + private static readonly ObjectPool.Builder> s_pooledBuilders = new(() => ImmutableSegmentedList.CreateBuilder()); + protected CSharpVirtualCharService() { } @@ -285,11 +287,20 @@ private static VirtualCharSequence CreateVirtualCharSequence( string tokenText, int offset, int startIndexInclusive, int endIndexExclusive, ArrayBuilder<(char ch, TextSpan span)> charResults) { // Second pass. Convert those characters to Runes. - var runeResults = ImmutableSegmentedList.CreateBuilder(); + using var pooledRuneResults = s_pooledBuilders.GetPooledObject(); + var runeResults = pooledRuneResults.Object; - ConvertCharactersToRunes(charResults, runeResults); + try + { + ConvertCharactersToRunes(charResults, runeResults); - return CreateVirtualCharSequence(tokenText, offset, startIndexInclusive, endIndexExclusive, runeResults); + return CreateVirtualCharSequence(tokenText, offset, startIndexInclusive, endIndexExclusive, runeResults); + } + finally + { + // Ensure the builder is cleared out before releasing back to the pool. + runeResults.Clear(); + } } private static void ConvertCharactersToRunes(ArrayBuilder<(char ch, TextSpan span)> charResults, ImmutableSegmentedList.Builder runeResults) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ArgumentSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ArgumentSyntaxExtensions.cs index 4b3f3039139ef..d606b894ff809 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ArgumentSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ArgumentSyntaxExtensions.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Linq; -using System.Text.RegularExpressions; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs index 5f5a71587dcfa..45a5af43bbbde 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -902,4 +903,193 @@ public static bool IsDirectChildOfMemberAccessExpression(this ExpressionSyntax e public static bool InsideCrefReference(this ExpressionSyntax expression) => expression.FirstAncestorOrSelf() != null; + + public static ITypeSymbol? GetTargetType( + this ExpressionSyntax expression, + SemanticModel semanticModel, + CancellationToken cancellationToken) + { + var topExpression = expression.WalkUpParentheses(); + var parent = topExpression.Parent; + return parent switch + { + EqualsValueClauseSyntax equalsValue => GetTargetTypeForEqualsValueClause(equalsValue), + CastExpressionSyntax castExpression => GetTargetTypedForCastExpression(castExpression), + // a ? [1, 2, 3] : ... is target typed if either the other side is *not* a collection, + // or the entire ternary is target typed itself. + ConditionalExpressionSyntax conditionalExpression => GetTargetTypeForConditionalExpression(conditionalExpression, topExpression), + // Similar rules for switches. + SwitchExpressionArmSyntax switchExpressionArm => GetTargetTypeForSwitchExpressionArm(switchExpressionArm), + InitializerExpressionSyntax initializerExpression => GetTargetTypeForInitializerExpression(initializerExpression, topExpression), + CollectionElementSyntax collectionElement => GetTargetTypeForCollectionElement(collectionElement), + AssignmentExpressionSyntax assignmentExpression => GetTargetTypeForAssignmentExpression(assignmentExpression, topExpression), + BinaryExpressionSyntax binaryExpression => GetTargetTypeForBinaryExpression(binaryExpression, topExpression), + LambdaExpressionSyntax lambda => GetTargetTypeForLambdaExpression(lambda, topExpression), + ArgumentSyntax argument => GetTargetTypeForArgument(argument), + AttributeArgumentSyntax attributeArgument => GetTargetTypeForAttributeArgument(attributeArgument), + ReturnStatementSyntax returnStatement => GetTargetTypeForReturnStatement(returnStatement), + ArrowExpressionClauseSyntax arrowExpression => GetTargetTypeForArrowExpression(arrowExpression), + _ => null, + }; + + // return result is IErrorTypeSymbol ? null : result; + + bool HasType(ExpressionSyntax expression, [NotNullWhen(true)] out ITypeSymbol? type) + { + type = semanticModel.GetTypeInfo(expression, cancellationToken).Type; + return type is not null; // and not IErrorTypeSymbol; + } + + ITypeSymbol? GetTargetTypeForArgument(ArgumentSyntax argument) + { + if (argument.Parent is TupleExpressionSyntax tupleExpression) + { + var tupleType = tupleExpression.GetTargetType(semanticModel, cancellationToken); + if (tupleType is null) + return null; + + var typeArguments = tupleType.GetTypeArguments(); + var index = tupleExpression.Arguments.IndexOf(argument); + + return index < typeArguments.Length ? typeArguments[index] : null; + } + else + { + return argument.DetermineParameter(semanticModel, allowUncertainCandidates: false, allowParams: true, cancellationToken)?.Type; + } + } + + ITypeSymbol? GetTargetTypeForAttributeArgument(AttributeArgumentSyntax argument) + => argument.DetermineParameter(semanticModel, allowUncertainCandidates: false, allowParams: true, cancellationToken)?.Type; + + ITypeSymbol? GetTargetTypeForArrowExpression(ArrowExpressionClauseSyntax arrowExpression) + { + var parent = arrowExpression.GetRequiredParent(); + var symbol = semanticModel.GetSymbolInfo(parent, cancellationToken).Symbol ?? semanticModel.GetDeclaredSymbol(parent, cancellationToken); + return symbol.GetMemberType(); + } + + ITypeSymbol? GetTargetTypeForReturnStatement(ReturnStatementSyntax returnStatement) + { + for (SyntaxNode? current = returnStatement; current != null; current = current.Parent) + { + if (current.IsReturnableConstruct()) + { + var symbol = semanticModel.GetSymbolInfo(current, cancellationToken).Symbol ?? semanticModel.GetDeclaredSymbol(current, cancellationToken); + return symbol.GetMemberType(); + } + } + + return null; + } + + ITypeSymbol? GetTargetTypeForEqualsValueClause(EqualsValueClauseSyntax equalsValue) + { + // If we're after an `x = ...` and it's not `var x`, this is target typed. + if (equalsValue.Parent is VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Type.IsVar: true } }) + return null; + + var symbol = semanticModel.GetDeclaredSymbol(equalsValue.GetRequiredParent(), cancellationToken); + return symbol.GetMemberType(); + } + + ITypeSymbol? GetTargetTypedForCastExpression(CastExpressionSyntax castExpression) + => semanticModel.GetTypeInfo(castExpression.Type, cancellationToken).Type; + + ITypeSymbol? GetTargetTypeForConditionalExpression(ConditionalExpressionSyntax conditionalExpression, ExpressionSyntax expression) + { + if (conditionalExpression.WhenTrue == expression) + return HasType(conditionalExpression.WhenFalse, out var falseType) ? falseType : conditionalExpression.GetTargetType(semanticModel, cancellationToken); + else if (conditionalExpression.WhenFalse == expression) + return HasType(conditionalExpression.WhenTrue, out var trueType) ? trueType : conditionalExpression.GetTargetType(semanticModel, cancellationToken); + else + return null; + } + + ITypeSymbol? GetTargetTypeForLambdaExpression(LambdaExpressionSyntax lambda, ExpressionSyntax expression) + => lambda.ExpressionBody == expression && + lambda.GetTargetType(semanticModel, cancellationToken) is INamedTypeSymbol { DelegateInvokeMethod.ReturnType: var returnType } ? returnType : null; + + ITypeSymbol? GetTargetTypeForSwitchExpressionArm(SwitchExpressionArmSyntax switchExpressionArm) + { + var switchExpression = (SwitchExpressionSyntax)switchExpressionArm.GetRequiredParent(); + + // check if any other arm has a type that this would be target typed against. + foreach (var arm in switchExpression.Arms) + { + if (arm != switchExpressionArm && HasType(arm.Expression, out var armType)) + return armType; + } + + // All arms do not have a type, this is target typed if the switch itself is target typed. + return switchExpression.GetTargetType(semanticModel, cancellationToken); + } + + ITypeSymbol? GetTargetTypeForCollectionElement(CollectionElementSyntax collectionElement) + { + // We do not currently target type spread expressions in a collection expression. + if (collectionElement is not ExpressionElementSyntax) + return null; + + // The element it target typed if the parent collection is itself target typed. + var collectionExpression = (CollectionExpressionSyntax)collectionElement.GetRequiredParent(); + var collectionTargetType = collectionExpression.GetTargetType(semanticModel, cancellationToken); + if (collectionTargetType is null) + return null; + + if (collectionTargetType.IsSpanOrReadOnlySpan()) + return collectionTargetType.GetTypeArguments().Single(); + + var ienumerableType = semanticModel.Compilation.IEnumerableOfTType(); + if (collectionTargetType.OriginalDefinition.Equals(ienumerableType)) + return collectionTargetType.GetTypeArguments().Single(); + + foreach (var iface in collectionTargetType.AllInterfaces) + { + if (iface.OriginalDefinition.Equals(ienumerableType)) + return iface.TypeArguments.Single(); + } + + return null; + } + + ITypeSymbol? GetTargetTypeForInitializerExpression(InitializerExpressionSyntax initializerExpression, ExpressionSyntax expression) + { + // new X[] { [1, 2, 3] }. Elements are target typed by array type. + if (initializerExpression.Parent is ArrayCreationExpressionSyntax arrayCreation) + return HasType(arrayCreation.Type, out var elementType) ? elementType : null; + + // new [] { [1, 2, 3], ... }. Elements are target typed if there's another element with real type. + if (initializerExpression.Parent is ImplicitArrayCreationExpressionSyntax) + { + foreach (var sibling in initializerExpression.Expressions) + { + if (sibling != expression && HasType(sibling, out var siblingType)) + return siblingType; + } + + return null; + } + + // T[] x = [1, 2, 3]; + if (initializerExpression.Parent is EqualsValueClauseSyntax equalsValue) + return GetTargetTypeForEqualsValueClause(equalsValue); + + // TODO: Handle these. + if (initializerExpression.Parent is StackAllocArrayCreationExpressionSyntax or ImplicitStackAllocArrayCreationExpressionSyntax) + return null; + + return null; + } + + ITypeSymbol? GetTargetTypeForAssignmentExpression(AssignmentExpressionSyntax assignmentExpression, ExpressionSyntax expression) + { + return expression == assignmentExpression.Right && HasType(assignmentExpression.Left, out var leftType) ? leftType : null; + } + + ITypeSymbol? GetTargetTypeForBinaryExpression(BinaryExpressionSyntax binaryExpression, ExpressionSyntax expression) + { + return binaryExpression.Kind() == SyntaxKind.CoalesceExpression && binaryExpression.Right == expression && HasType(binaryExpression.Left, out var leftType) ? leftType : null; + } + } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/LanguageVersionExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/LanguageVersionExtensions.cs index 7f451630e9f61..d4aa3c168bb89 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/LanguageVersionExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/LanguageVersionExtensions.cs @@ -6,6 +6,9 @@ namespace Microsoft.CodeAnalysis.CSharp.Shared.Extensions; internal static class LanguageVersionExtensions { + public static bool IsCSharp14OrAbove(this LanguageVersion languageVersion) + => languageVersion >= LanguageVersion.Preview; + public static bool IsCSharp13OrAbove(this LanguageVersion languageVersion) => languageVersion >= LanguageVersion.CSharp13; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/MemberDeclarationSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/MemberDeclarationSyntaxExtensions.cs index b1cf56d09951c..a8d8ba4ab5a73 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/MemberDeclarationSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/MemberDeclarationSyntaxExtensions.cs @@ -167,11 +167,7 @@ public static bool HasMethodShape(this MemberDeclarationSyntax memberDeclaration => memberDeclaration is BaseMethodDeclarationSyntax; public static BlockSyntax GetBody(this MemberDeclarationSyntax memberDeclaration) - => memberDeclaration switch - { - BaseMethodDeclarationSyntax method => method.Body, - _ => null, - }; + => (memberDeclaration as BaseMethodDeclarationSyntax)?.Body; public static ArrowExpressionClauseSyntax GetExpressionBody(this MemberDeclarationSyntax memberDeclaration) => memberDeclaration switch diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs index 02042426e6d6c..4c3dbcd9d9ef9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs @@ -111,6 +111,10 @@ public static bool CanRemoveParentheses( if (expression.IsKind(SyntaxKind.TupleExpression)) return true; + // ([...]) -> [...] + if (expression.IsKind(SyntaxKind.CollectionExpression)) + return true; + // int Prop => (x); -> int Prop => x; if (nodeParent is ArrowExpressionClauseSyntax arrowExpressionClause && arrowExpressionClause.Expression == node) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs index c76a5a8def8fa..ebd2ccc4e5b32 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs @@ -166,22 +166,18 @@ private static void AppendAliasNames(IEnumerable } } - public ForEachSymbols GetForEachSymbols(SemanticModel semanticModel, SyntaxNode forEachStatement) + public ForEachSymbols GetForEachSymbols(SemanticModel semanticModel, SyntaxNode node) { - if (forEachStatement is CommonForEachStatementSyntax csforEachStatement) - { - var info = semanticModel.GetForEachStatementInfo(csforEachStatement); - return new ForEachSymbols( - info.GetEnumeratorMethod, - info.MoveNextMethod, - info.CurrentProperty, - info.DisposeMethod, - info.ElementType); - } - else - { + if (node is not CommonForEachStatementSyntax forEachStatement) return default; - } + + var info = semanticModel.GetForEachStatementInfo(forEachStatement); + return new ForEachSymbols( + info.GetEnumeratorMethod, + info.MoveNextMethod, + info.CurrentProperty, + info.DisposeMethod, + info.ElementType); } public SymbolInfo GetCollectionInitializerSymbolInfo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) @@ -233,8 +229,7 @@ private static void FlattenDeconstructionMethods(DeconstructionInfo deconstructi public bool IsPartial(INamedTypeSymbol typeSymbol, CancellationToken cancellationToken) { - var syntaxRefs = typeSymbol.DeclaringSyntaxReferences; - foreach (var syntaxRef in syntaxRefs) + foreach (var syntaxRef in typeSymbol.DeclaringSyntaxReferences) { var node = syntaxRef.GetSyntax(cancellationToken); if (node is BaseTypeDeclarationSyntax { Modifiers: { } modifiers } && @@ -247,23 +242,15 @@ public bool IsPartial(INamedTypeSymbol typeSymbol, CancellationToken cancellatio return false; } - public IEnumerable GetDeclaredSymbols( - SemanticModel semanticModel, SyntaxNode memberDeclaration, CancellationToken cancellationToken) - { - switch (memberDeclaration) + public IEnumerable GetDeclaredSymbols(SemanticModel semanticModel, SyntaxNode memberDeclaration, CancellationToken cancellationToken) + => memberDeclaration switch { - case FieldDeclarationSyntax field: - return field.Declaration.Variables.Select( - v => semanticModel.GetRequiredDeclaredSymbol(v, cancellationToken)); - - case EventFieldDeclarationSyntax eventField: - return eventField.Declaration.Variables.Select( - v => semanticModel.GetRequiredDeclaredSymbol(v, cancellationToken)); - - default: - return [semanticModel.GetRequiredDeclaredSymbol(memberDeclaration, cancellationToken)]; - } - } + FieldDeclarationSyntax field + => field.Declaration.Variables.Select(v => semanticModel.GetRequiredDeclaredSymbol(v, cancellationToken)), + EventFieldDeclarationSyntax eventField + => eventField.Declaration.Variables.Select(v => semanticModel.GetRequiredDeclaredSymbol(v, cancellationToken)), + _ => [semanticModel.GetRequiredDeclaredSymbol(memberDeclaration, cancellationToken)], + }; public IParameterSymbol? FindParameterForArgument(SemanticModel semanticModel, SyntaxNode argument, bool allowUncertainCandidates, bool allowParams, CancellationToken cancellationToken) => ((ArgumentSyntax)argument).DetermineParameter(semanticModel, allowUncertainCandidates, allowParams, cancellationToken); @@ -306,7 +293,7 @@ static ImmutableArray GetCallingConventionSymbols(SemanticModel model, /// heuristics to provide a better result for tokens that users conceptually think bind to things, but which the /// compiler does not necessarily return results for. /// - private static ImmutableArray GetSymbolInfo(SemanticModel semanticModel, SyntaxNode node, SyntaxToken token, CancellationToken cancellationToken) + private ImmutableArray GetSymbolInfo(SemanticModel semanticModel, SyntaxNode node, SyntaxToken token, CancellationToken cancellationToken) { switch (node) { @@ -368,11 +355,14 @@ private static ImmutableArray GetSymbolInfo(SemanticModel semanticModel type.Equals(symbol, SymbolEqualityComparer.Default) && !type.Equals(symbol, SymbolEqualityComparer.IncludeNullability)) { - return ImmutableArray.Create(type); + return [type]; } } - return semanticModel.GetSymbolInfo(node, cancellationToken).GetBestOrAllSymbols(); + var preprocessingSymbol = GetPreprocessingSymbol(semanticModel, node); + return preprocessingSymbol != null + ? [preprocessingSymbol] + : semanticModel.GetSymbolInfo(node, cancellationToken).GetBestOrAllSymbols(); } public bool IsInsideNameOfExpression(SemanticModel semanticModel, [NotNullWhen(true)] SyntaxNode? node, CancellationToken cancellationToken) @@ -405,6 +395,25 @@ public bool IsInExpressionTree(SemanticModel semanticModel, SyntaxNode node, [No public string GenerateNameForExpression(SemanticModel semanticModel, SyntaxNode expression, bool capitalize, CancellationToken cancellationToken) => semanticModel.GenerateNameForExpression((ExpressionSyntax)expression, capitalize, cancellationToken); + public IPreprocessingSymbol? GetPreprocessingSymbol(SemanticModel semanticModel, SyntaxNode node) + => node switch + { + IdentifierNameSyntax nameSyntax when IsInPreprocessingSymbolContext(nameSyntax) => CreatePreprocessingSymbol(semanticModel, nameSyntax.Identifier), + DefineDirectiveTriviaSyntax defineSyntax => CreatePreprocessingSymbol(semanticModel, defineSyntax.Name), + UndefDirectiveTriviaSyntax undefSyntax => CreatePreprocessingSymbol(semanticModel, undefSyntax.Name), + _ => null, + }; + + private static IPreprocessingSymbol? CreatePreprocessingSymbol(SemanticModel model, SyntaxToken identifier) + => model.Compilation.CreatePreprocessingSymbol(identifier.ValueText); + + private static bool IsInPreprocessingSymbolContext(SyntaxNode node) + => node.Ancestors().Any(n => n.Kind() is + SyntaxKind.IfDirectiveTrivia or + SyntaxKind.ElifDirectiveTrivia or + SyntaxKind.DefineDirectiveTrivia or + SyntaxKind.UndefDirectiveTrivia); + #if !CODE_STYLE public async Task GetInterceptorSymbolAsync(Document document, int position, CancellationToken cancellationToken) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index 74652e0c0418f..b22d533aaec0d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -1196,6 +1196,13 @@ public SyntaxList GetContentsOfInterpolatedString(SyntaxNode interpo public bool IsVerbatimStringLiteral(SyntaxToken token) => token.IsVerbatimStringLiteral(); + public bool IsRawStringLiteral(SyntaxToken token) + => token.Kind() is + SyntaxKind.SingleLineRawStringLiteralToken or + SyntaxKind.MultiLineRawStringLiteralToken or + SyntaxKind.Utf8SingleLineRawStringLiteralToken or + SyntaxKind.Utf8MultiLineRawStringLiteralToken; + public bool IsNumericLiteral(SyntaxToken token) => token.Kind() == SyntaxKind.NumericLiteralToken; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs index 3aefcfa76563a..bef0199ad2181 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs @@ -39,6 +39,8 @@ public int Convert(TSyntaxKind kind) where TSyntaxKind : struct public int RegionDirectiveTrivia => (int)SyntaxKind.RegionDirectiveTrivia; public int EndRegionDirectiveTrivia => (int)SyntaxKind.EndRegionDirectiveTrivia; public int? ShebangDirectiveTrivia => (int)SyntaxKind.ShebangDirectiveTrivia; + public int DefineDirectiveTrivia => (int)SyntaxKind.DefineDirectiveTrivia; + public int? UndefDirectiveTrivia => (int)SyntaxKind.UndefDirectiveTrivia; public int CloseBraceToken => (int)SyntaxKind.CloseBraceToken; public int? CloseBracketToken => (int)SyntaxKind.CloseBracketToken; @@ -112,6 +114,7 @@ public int Convert(TSyntaxKind kind) where TSyntaxKind : struct public int ReferenceEqualsExpression => (int)SyntaxKind.EqualsExpression; public int ReferenceNotEqualsExpression => (int)SyntaxKind.NotEqualsExpression; public int SimpleMemberAccessExpression => (int)SyntaxKind.SimpleMemberAccessExpression; + public int? SuppressNullableWarningExpression => (int)SyntaxKind.SuppressNullableWarningExpression; public int TernaryConditionalExpression => (int)SyntaxKind.ConditionalExpression; public int ThisExpression => (int)SyntaxKind.ThisExpression; public int? ThrowExpression => (int)SyntaxKind.ThrowExpression; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs index 251d02fcc361a..b8933465c52de 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs @@ -506,9 +506,27 @@ private static bool IsConversionCastSafeToRemove( return false; } - // If the types of the expressions are the same, then we can remove safely. - if (originalConvertedType.Equals(rewrittenConvertedType, SymbolEqualityComparer.IncludeNullability)) - return true; + if (originalConvertedType.Equals(rewrittenConvertedType)) + { + // If the types of the expressions are exactly the same, then we can remove safely. + if (originalConvertedType.Equals(rewrittenConvertedType, SymbolEqualityComparer.IncludeNullability)) + return true; + + // The types differ on nullability. But we may still want to remove this. + // + // For example: + // + // string Method() => (string?)notNullString; + // + // Here we have a non-null type converted to its nullable form, which is target typed back to the non-null + // type. Removing this nullable cast is safe and desirable. + var targetType = castNode.GetTargetType(originalSemanticModel, cancellationToken); + if (targetType is not null and not IErrorTypeSymbol && + rewrittenConvertedType.Equals(targetType, SymbolEqualityComparer.IncludeNullability)) + { + return true; + } + } // We can safely remove convertion to object in interpolated strings regardless of nullability if (castNode.IsParentKind(SyntaxKind.Interpolation) && originalConversionOperation.Type?.SpecialType is SpecialType.System_Object) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index 6b6714a96ee97..e47ce24e5224c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -475,6 +475,7 @@ + diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs index 6a023012023e1..64152ff9a6ad2 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions.cs @@ -72,8 +72,12 @@ public static SymbolVisibility GetResultantVisibility(this ISymbol symbol) return visibility; } - public static ISymbol? GetOverriddenMember(this ISymbol? symbol) - => symbol switch + public static ISymbol? GetOverriddenMember(this ISymbol? symbol, bool allowLooseMatch = false) + { + if (symbol is null) + return null; + + ISymbol? exactMatch = symbol switch { IMethodSymbol method => method.OverriddenMethod, IPropertySymbol property => property.OverriddenProperty, @@ -81,6 +85,54 @@ public static SymbolVisibility GetResultantVisibility(this ISymbol symbol) _ => null, }; + if (exactMatch != null) + return exactMatch; + + if (allowLooseMatch && + (symbol.IsVirtual || symbol.IsAbstract || symbol.IsOverride)) + { + foreach (var baseType in symbol.ContainingType.GetBaseTypes()) + { + if (TryFindLooseMatch(symbol, baseType, out var looseMatch)) + return looseMatch; + } + } + + return null; + + bool TryFindLooseMatch(ISymbol symbol, INamedTypeSymbol baseType, [NotNullWhen(true)] out ISymbol? looseMatch) + { + IMethodSymbol? bestMethod = null; + var parameterCount = symbol.GetParameters().Length; + + foreach (var member in baseType.GetMembers(symbol.Name)) + { + if (member.Kind != symbol.Kind) + continue; + + if (!member.IsOverridable()) + continue; + + if (symbol.Kind is SymbolKind.Event or SymbolKind.Property) + { + // We've found a matching event/property in the base type (perhaps differing by return type). This + // is a good enough match to return as a loose match for the starting symbol. + looseMatch = member; + return true; + } + else if (member is IMethodSymbol method) + { + // Prefer methods that are closed in parameter count to the original method we started with. + if (bestMethod is null || Math.Abs(method.Parameters.Length - parameterCount) < Math.Abs(bestMethod.Parameters.Length - parameterCount)) + bestMethod = method; + } + } + + looseMatch = bestMethod; + return looseMatch != null; + } + } + public static ImmutableArray ExplicitInterfaceImplementations(this ISymbol symbol) => symbol switch { @@ -109,11 +161,9 @@ public static ImmutableArray ImplicitInterfaceImplementations(this ISym public static bool IsOverridable([NotNullWhen(true)] this ISymbol? symbol) { - // Members can only have overrides if they are virtual, abstract or override and is not - // sealed. - return symbol?.ContainingType?.TypeKind == TypeKind.Class && - (symbol.IsVirtual || symbol.IsAbstract || symbol.IsOverride) && - !symbol.IsSealed; + // Members can only have overrides if they are virtual, abstract or override and is not sealed. + return symbol is { ContainingType.TypeKind: TypeKind.Class, IsSealed: false } && + (symbol.IsVirtual || symbol.IsAbstract || symbol.IsOverride); } public static bool IsImplementableMember([NotNullWhen(true)] this ISymbol? symbol) @@ -280,6 +330,7 @@ public static bool IsRequired([NotNullWhen(true)] this ISymbol? symbol) IMethodSymbol methodSymbol => methodSymbol.ReturnType, IEventSymbol eventSymbol => eventSymbol.Type, IParameterSymbol parameterSymbol => parameterSymbol.Type, + ILocalSymbol localSymbol => localSymbol.Type, _ => null, }; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs index c3db3f7a06779..1de694e2114dc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs @@ -121,9 +121,9 @@ public static IEnumerable GetBaseTypesAndThis(this ITypeSymbol? typ } } - public static IEnumerable GetBaseTypes(this ITypeSymbol type) + public static IEnumerable GetBaseTypes(this ITypeSymbol? type) { - var current = type.BaseType; + var current = type?.BaseType; while (current != null) { yield return current; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs index 4a05b3ffbfe13..48ba16001283e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -107,9 +108,7 @@ public static int IndexOf(this IList list, Func predicate) for (var i = 0; i < list.Count; i++) { if (predicate(list[i])) - { return i; - } } return -1; @@ -120,9 +119,7 @@ public static int IndexOf(this IList list, Func predi for (var i = 0; i < list.Count; i++) { if (predicate(list[i], arg)) - { return i; - } } return -1; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs index 83886f8fdf3b6..15700c2a11709 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs @@ -223,6 +223,11 @@ private void AddTokenOperations( using (Logger.LogBlock(FunctionId.Formatting_CollectTokenOperation, cancellationToken)) { + // Grow the SegmentedList in a single allocation if the resultant list is going to be + // significantly larger than it's existing Capacity. + if (tokenStream.TokenCount > 2 * list.Capacity) + list.EnsureCapacity(tokenStream.TokenCount); + foreach (var (index, currentToken, nextToken) in tokenStream.TokenIterator) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyle.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyle.cs index 9e64f37819f53..57d4d16d1a82e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyle.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyle.cs @@ -427,9 +427,7 @@ private string EnsureSuffix(string name) for (var i = Suffix.Length; i > 0; i--) { if (name.EndsWith(Suffix[..i])) - { return name + Suffix[i..]; - } } return name + Suffix; @@ -437,15 +435,26 @@ private string EnsureSuffix(string name) private string EnsurePrefix(string name) { + // Exceptional cases. If the name is some interface name (like `InputStream`) and the rule is to have a single + // character prefix like "Add `I` for interfaces" don't consider the existing 'I' to be a match of the prefix. + + if (Prefix is [var prefixChar] && + char.IsUpper(prefixChar) && + name is [var nameChar1, var nameChar2, ..] && + prefixChar == nameChar1 && + char.IsLower(nameChar2)) + { + // return IInputStream here, even though InputStream already starts with 'I'. + return Prefix + name; + } + // If the name already starts with any suffix of the Prefix, only prepend the prefix of // the Prefix not contained in the longest such Prefix suffix. For example, if the // required prefix is "catdog_" and the name is "dog_test", then only prepend "cat". for (var i = 0; i < Prefix.Length; i++) { if (name.StartsWith(Prefix[i..])) - { return Prefix[..i] + name; - } } return Prefix + name; @@ -480,13 +489,11 @@ public void WriteTo(ObjectWriter writer) } public static NamingStyle ReadFrom(ObjectReader reader) - { - return new NamingStyle( + => new( reader.ReadGuid(), reader.ReadString(), reader.ReadString(), reader.ReadString(), reader.ReadString(), (Capitalization)reader.ReadInt32()); - } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs index 1a12537d7fbdc..7f05d5afec83f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs @@ -110,6 +110,17 @@ internal partial interface ISemanticFacts string GenerateNameForExpression(SemanticModel semanticModel, SyntaxNode expression, bool capitalize, CancellationToken cancellationToken); + /// + /// Gets the that the given node involves. + /// The node's kind must match any of the following kinds: + /// + /// , + /// , or + /// . + /// + /// + IPreprocessingSymbol? GetPreprocessingSymbol(SemanticModel semanticModel, SyntaxNode node); + #if !CODE_STYLE /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs index cd199f1d8fabd..d4b01afb7e636 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs @@ -162,6 +162,7 @@ internal interface ISyntaxFacts bool IsNumericLiteral(SyntaxToken token); bool IsVerbatimStringLiteral(SyntaxToken token); + bool IsRawStringLiteral(SyntaxToken token); bool IsUsingOrExternOrImport([NotNullWhen(true)] SyntaxNode? node); bool IsGlobalAssemblyAttribute([NotNullWhen(true)] SyntaxNode? node); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs index ddaf52a46398e..432397875f90d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs @@ -41,6 +41,8 @@ internal interface ISyntaxKinds int EndRegionDirectiveTrivia { get; } int RegionDirectiveTrivia { get; } int? ShebangDirectiveTrivia { get; } + int DefineDirectiveTrivia { get; } + int? UndefDirectiveTrivia { get; } #endregion @@ -155,6 +157,7 @@ internal interface ISyntaxKinds int ReferenceEqualsExpression { get; } int ReferenceNotEqualsExpression { get; } int SimpleMemberAccessExpression { get; } + int? SuppressNullableWarningExpression { get; } int TernaryConditionalExpression { get; } int ThisExpression { get; } int? ThrowExpression { get; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamespaceSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamespaceSymbolKey.cs index e5c38934a739a..c9126832e26e9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamespaceSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamespaceSymbolKey.cs @@ -76,7 +76,7 @@ protected sealed override SymbolKeyResolution Resolve( if (containingSymbolFailureReason != null) { - failureReason = $"({nameof(EventSymbolKey)} {nameof(containingSymbolResolution)} failed -> {containingSymbolFailureReason})"; + failureReason = $"({nameof(NamespaceSymbolKey)} {nameof(containingSymbolResolution)} failed -> {containingSymbolFailureReason})"; return default; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs new file mode 100644 index 0000000000000..11137f869770f --- /dev/null +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.PreprocessingSymbolKey.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis; + +internal partial struct SymbolKey +{ + private sealed class PreprocessingSymbolKey : AbstractSymbolKey + { + public static readonly PreprocessingSymbolKey Instance = new(); + + public sealed override void Create(IPreprocessingSymbol symbol, SymbolKeyWriter visitor) + => visitor.WriteString(symbol.Name); + + protected sealed override SymbolKeyResolution Resolve(SymbolKeyReader reader, IPreprocessingSymbol? contextualSymbol, out string? failureReason) + { + failureReason = null; + return new SymbolKeyResolution(reader.Compilation.CreatePreprocessingSymbol(reader.ReadRequiredString())); + } + } +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyReader.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyReader.cs index 4ee4ab1341c10..8e99117ea7fd8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyReader.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyReader.cs @@ -507,28 +507,29 @@ private SymbolKeyResolution ReadWorker(SymbolKeyType type, out string? failureRe => type switch { SymbolKeyType.Alias => AliasSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.AnonymousFunctionOrDelegate => AnonymousFunctionOrDelegateSymbolKey.Resolve(this, out failureReason), + SymbolKeyType.AnonymousType => AnonymousTypeSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.ArrayType => ArrayTypeSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.Assembly => AssemblySymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.BodyLevel => BodyLevelSymbolKey.Resolve(this, out failureReason), + SymbolKeyType.BuiltinOperator => BuiltinOperatorSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.ConstructedMethod => ConstructedMethodSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.NamedType => NamedTypeSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.DynamicType => DynamicTypeSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.ErrorType => ErrorTypeSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.Event => EventSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.Field => FieldSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.FunctionPointer => FunctionPointerTypeSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.DynamicType => DynamicTypeSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.BuiltinOperator => BuiltinOperatorSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.Method => MethodSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.Module => ModuleSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.NamedType => NamedTypeSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.Namespace => NamespaceSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.PointerType => PointerTypeSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.Parameter => ParameterSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.PointerType => PointerTypeSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.Preprocessing => PreprocessingSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.Property => PropertySymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.ArrayType => ArrayTypeSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.Assembly => AssemblySymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.TupleType => TupleTypeSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.Module => ModuleSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.Event => EventSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.ReducedExtensionMethod => ReducedExtensionMethodSymbolKey.Instance.Resolve(this, out failureReason), + SymbolKeyType.TupleType => TupleTypeSymbolKey.Instance.Resolve(this, out failureReason), SymbolKeyType.TypeParameter => TypeParameterSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.AnonymousType => AnonymousTypeSymbolKey.Instance.Resolve(this, out failureReason), - SymbolKeyType.AnonymousFunctionOrDelegate => AnonymousFunctionOrDelegateSymbolKey.Resolve(this, out failureReason), SymbolKeyType.TypeParameterOrdinal => TypeParameterOrdinalSymbolKey.Resolve(this, out failureReason), _ => throw new NotImplementedException(), }; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs index a0145d6ffd2d5..9d9735c7b6c17 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs @@ -20,28 +20,29 @@ internal partial struct SymbolKey private enum SymbolKeyType { Alias = 'A', + AnonymousFunctionOrDelegate = 'Z', + AnonymousType = 'W', + ArrayType = 'R', + Assembly = 'S', BodyLevel = 'B', + BuiltinOperator = 'L', ConstructedMethod = 'C', - NamedType = 'D', + DynamicType = 'I', ErrorType = 'E', + Event = 'V', Field = 'F', FunctionPointer = 'G', - DynamicType = 'I', - BuiltinOperator = 'L', Method = 'M', + Module = 'U', + NamedType = 'D', Namespace = 'N', - PointerType = 'O', Parameter = 'P', + PointerType = 'O', + Preprocessing = 'J', Property = 'Q', - ArrayType = 'R', - Assembly = 'S', - TupleType = 'T', - Module = 'U', - Event = 'V', - AnonymousType = 'W', ReducedExtensionMethod = 'X', + TupleType = 'T', TypeParameter = 'Y', - AnonymousFunctionOrDelegate = 'Z', // Not to be confused with ArrayType. This indicates an array of elements in the stream. Array = '%', @@ -190,6 +191,11 @@ internal void WriteSymbolKey(ISymbol? symbol) // ID. id = existingId; } + else if (symbol is IPreprocessingSymbol preprocessingSymbol) + { + WriteType(SymbolKeyType.Preprocessing); + PreprocessingSymbolKey.Instance.Create(preprocessingSymbol, this); + } else { // Haven't hit this symbol before, write out its fresh ID. @@ -473,7 +479,7 @@ public override void VisitEvent(IEventSymbol eventSymbol) public override void VisitTypeParameter(ITypeParameterSymbol typeParameterSymbol) { // If it's a reference to a method type parameter, and we're currently writing - // out a signture, then only write out the ordinal of type parameter. This + // out a signature, then only write out the ordinal of type parameter. This // helps prevent recursion problems in cases like "Goo(T t). if (ShouldWriteTypeParameterOrdinal(typeParameterSymbol, out var methodIndex)) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs index 3412917555406..d993f44ba3e89 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs @@ -66,6 +66,8 @@ namespace Microsoft.CodeAnalysis; /// Two s are the "same" if they have /// the "same" and /// the "same" . +/// Two s are the "same" if they have +/// the "same" . /// /// /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb index 2c88bbda04e29..4a4790a665427 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb @@ -258,9 +258,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function Public Function GetBestOrAllSymbols(semanticModel As SemanticModel, node As SyntaxNode, token As SyntaxToken, cancellationToken As CancellationToken) As ImmutableArray(Of ISymbol) Implements ISemanticFacts.GetBestOrAllSymbols - Return If(node Is Nothing, - ImmutableArray(Of ISymbol).Empty, - semanticModel.GetSymbolInfo(node, cancellationToken).GetBestOrAllSymbols()) + If node Is Nothing Then + Return ImmutableArray(Of ISymbol).Empty + End If + + Dim preprocessingSymbol = semanticModel.GetPreprocessingSymbolInfo(node).Symbol + Return If(preprocessingSymbol IsNot Nothing, + ImmutableArray.Create(Of ISymbol)(preprocessingSymbol), + semanticModel.GetSymbolInfo(node, cancellationToken).GetBestOrAllSymbols()) End Function Public Function IsInsideNameOfExpression(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFacts.IsInsideNameOfExpression @@ -283,6 +288,46 @@ Namespace Microsoft.CodeAnalysis.VisualBasic DirectCast(expression, ExpressionSyntax), capitalize, cancellationToken) End Function + Public Function GetPreprocessingSymbol(model As SemanticModel, node As SyntaxNode) As IPreprocessingSymbol Implements ISemanticFacts.GetPreprocessingSymbol + Dim nameSyntax = TryCast(node, IdentifierNameSyntax) + If nameSyntax IsNot Nothing Then + If IsWithinPreprocessorConditionalExpression(nameSyntax) Then + Return CreatePreprocessingSymbol(model, nameSyntax.Identifier) + End If + End If + + Dim constSyntax = TryCast(node, ConstDirectiveTriviaSyntax) + If constSyntax IsNot Nothing Then + Return CreatePreprocessingSymbol(model, constSyntax.Name) + End If + + Return Nothing + End Function + + Private Shared Function CreatePreprocessingSymbol(model As SemanticModel, token As SyntaxToken) As IPreprocessingSymbol + Return model.Compilation.CreatePreprocessingSymbol(token.ValueText) + End Function + + Friend Shared Function IsWithinPreprocessorConditionalExpression(node As IdentifierNameSyntax) As Boolean + Debug.Assert(node IsNot Nothing) + Dim current As SyntaxNode = node + Dim parent As SyntaxNode = node.Parent + + While parent IsNot Nothing + Select Case parent.Kind() + Case SyntaxKind.IfDirectiveTrivia, SyntaxKind.ElseIfDirectiveTrivia + Return DirectCast(parent, IfDirectiveTriviaSyntax).Condition Is current + Case SyntaxKind.ConstDirectiveTrivia + Return DirectCast(parent, ConstDirectiveTriviaSyntax).Value Is current + Case Else + current = parent + parent = current.Parent + End Select + End While + + Return False + End Function + #If Not CODE_STYLE Then Public Function GetInterceptorSymbolAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of ISymbol) Implements ISemanticFacts.GetInterceptorSymbolAsync diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb index bf8f406ff26f6..f763bf800fc50 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb @@ -1252,6 +1252,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Return False End Function + Public Function IsRawStringLiteral(token As SyntaxToken) As Boolean Implements ISyntaxFacts.IsRawStringLiteral + ' VB does not have raw strings + Return False + End Function + Public Function GetArgumentsOfObjectCreationExpression(node As SyntaxNode) As SeparatedSyntaxList(Of SyntaxNode) Implements ISyntaxFacts.GetArgumentsOfObjectCreationExpression Dim argumentList = DirectCast(node, ObjectCreationExpressionSyntax).ArgumentList Return If(argumentList Is Nothing, Nothing, GetArgumentsOfArgumentList(argumentList)) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb index b6de084b165a3..0bb05a16e1f5b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb @@ -41,6 +41,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Public ReadOnly Property RegionDirectiveTrivia As Integer = SyntaxKind.RegionDirectiveTrivia Implements ISyntaxKinds.RegionDirectiveTrivia Public ReadOnly Property EndRegionDirectiveTrivia As Integer = SyntaxKind.EndRegionDirectiveTrivia Implements ISyntaxKinds.EndRegionDirectiveTrivia Public ReadOnly Property ShebangDirectiveTrivia As Integer? Implements ISyntaxKinds.ShebangDirectiveTrivia + Public ReadOnly Property DefineDirectiveTrivia As Integer = SyntaxKind.ConstDirectiveTrivia Implements ISyntaxKinds.DefineDirectiveTrivia + Public ReadOnly Property UndefDirectiveTrivia As Integer? Implements ISyntaxKinds.UndefDirectiveTrivia Public ReadOnly Property CloseBraceToken As Integer = SyntaxKind.CloseBraceToken Implements ISyntaxKinds.CloseBraceToken Public ReadOnly Property CloseBracketToken As Integer? = Nothing Implements ISyntaxKinds.CloseBracketToken @@ -114,6 +116,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Public ReadOnly Property ReferenceEqualsExpression As Integer = SyntaxKind.IsExpression Implements ISyntaxKinds.ReferenceEqualsExpression Public ReadOnly Property ReferenceNotEqualsExpression As Integer = SyntaxKind.IsNotExpression Implements ISyntaxKinds.ReferenceNotEqualsExpression Public ReadOnly Property SimpleMemberAccessExpression As Integer = SyntaxKind.SimpleMemberAccessExpression Implements ISyntaxKinds.SimpleMemberAccessExpression + Public ReadOnly Property SuppressNullableWarningExpression As Integer? Implements ISyntaxKinds.SuppressNullableWarningExpression Public ReadOnly Property TernaryConditionalExpression As Integer = SyntaxKind.TernaryConditionalExpression Implements ISyntaxKinds.TernaryConditionalExpression Public ReadOnly Property ThisExpression As Integer = SyntaxKind.MeExpression Implements ISyntaxKinds.ThisExpression Public ReadOnly Property ThrowExpression As Integer? Implements ISyntaxKinds.ThrowExpression diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ConversionGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ConversionGenerator.cs index 62452baed9146..d8fa523840519 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ConversionGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ConversionGenerator.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Extensions; @@ -48,34 +49,31 @@ private static ConversionOperatorDeclarationSyntax GenerateConversionDeclaration CSharpCodeGenerationContextInfo info, CancellationToken cancellationToken) { - var hasNoBody = !info.Context.GenerateMethodBodies || method.IsExtern; - var reusableSyntax = GetReuseableSyntaxNodeForSymbol(method, info); if (reusableSyntax != null) - { return reusableSyntax; - } var keyword = method.MetadataName == WellKnownMemberNames.ImplicitConversionName ? ImplicitKeyword : ExplicitKeyword; - var checkedToken = SyntaxFacts.IsCheckedOperator(method.MetadataName) + var checkedKeyword = SyntaxFacts.IsCheckedOperator(method.MetadataName) ? CheckedKeyword : default; + var hasNoBody = !info.Context.GenerateMethodBodies || method.IsExtern; var declaration = ConversionOperatorDeclaration( attributeLists: AttributeGenerator.GenerateAttributeLists(method.GetAttributes(), info), - modifiers: GenerateModifiers(destination), + modifiers: GenerateModifiers(destination, method), implicitOrExplicitKeyword: keyword, - explicitInterfaceSpecifier: null, + explicitInterfaceSpecifier: GenerateExplicitInterfaceSpecifier(method.ExplicitInterfaceImplementations), operatorKeyword: OperatorKeyword, - checkedKeyword: checkedToken, + checkedKeyword: checkedKeyword, type: method.ReturnType.GenerateTypeSyntax(), parameterList: ParameterGenerator.GenerateParameterList(method.Parameters, isExplicit: false, info: info), body: hasNoBody ? null : StatementGenerator.GenerateBlock(method), expressionBody: null, - semicolonToken: hasNoBody ? SemicolonToken : new SyntaxToken()); + semicolonToken: hasNoBody ? SemicolonToken : default); declaration = UseExpressionBodyIfDesired(info, declaration, cancellationToken); @@ -100,11 +98,19 @@ private static ConversionOperatorDeclarationSyntax UseExpressionBodyIfDesired( return declaration; } - private static SyntaxTokenList GenerateModifiers(CodeGenerationDestination destination) + private static SyntaxTokenList GenerateModifiers(CodeGenerationDestination destination, IMethodSymbol method) { - // If these appear in interfaces they must be static abstract - return destination is CodeGenerationDestination.InterfaceType - ? ([StaticKeyword, AbstractKeyword]) - : ([PublicKeyword, StaticKeyword]); + // Only "static" allowed if we're an explicit impl. + if (method.ExplicitInterfaceImplementations.Any()) + { + return method.IsStatic ? [StaticKeyword] : []; + } + else + { + // If these appear in interfaces they must be static abstract + return destination is CodeGenerationDestination.InterfaceType + ? ([StaticKeyword, AbstractKeyword]) + : ([PublicKeyword, StaticKeyword]); + } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ParameterGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ParameterGenerator.cs index dde1cec02d278..4335961372e21 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ParameterGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ParameterGenerator.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration; @@ -76,7 +75,7 @@ internal static ParameterSyntax GetParameter(IParameterSymbol parameter, CSharpC private static SyntaxTokenList GenerateModifiers( IParameterSymbol parameter, bool isFirstParam) { - var list = CSharpSyntaxGeneratorInternal.GetParameterModifiers(parameter.RefKind); + var list = CSharpSyntaxGeneratorInternal.GetParameterModifiers(parameter); if (isFirstParam && parameter.ContainingSymbol is IMethodSymbol methodSymbol && diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.TypeSyntaxGeneratorVisitor.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.TypeSyntaxGeneratorVisitor.cs index c7d6581c6c6a4..22b6c9ab6bdce 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.TypeSyntaxGeneratorVisitor.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.TypeSyntaxGeneratorVisitor.cs @@ -161,10 +161,10 @@ static FunctionPointerUnmanagedCallingConventionSyntax GetConventionForString(st => FunctionPointerUnmanagedCallingConvention(Identifier(identifier)); } - var parameters = symbol.Signature.Parameters.Select(p => (p.Type, RefKindModifiers: CSharpSyntaxGeneratorInternal.GetParameterModifiers(p.RefKind))) + var parameters = symbol.Signature.Parameters.Select(p => (p.Type, RefKindModifiers: CSharpSyntaxGeneratorInternal.GetParameterModifiers(p))) .Concat([( Type: symbol.Signature.ReturnType, - RefKindModifiers: CSharpSyntaxGeneratorInternal.GetParameterModifiers(symbol.Signature.RefKind, forFunctionPointerReturnParameter: true))]) + RefKindModifiers: CSharpSyntaxGeneratorInternal.GetParameterModifiers(isScoped: false, symbol.Signature.RefKind, forFunctionPointerReturnParameter: true))]) .SelectAsArray(t => FunctionPointerParameter(t.Type.GenerateTypeSyntax()).WithModifiers(t.RefKindModifiers)); return AddInformationTo( diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpBlockFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpBlockFactsService.cs index 2d55c80232afa..75391d62201aa 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpBlockFactsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpBlockFactsService.cs @@ -11,11 +11,6 @@ namespace Microsoft.CodeAnalysis.CSharp; [ExportLanguageService(typeof(IBlockFactsService), LanguageNames.CSharp), Shared] -internal class CSharpBlockFactsService : CSharpBlockFacts, IBlockFactsService -{ - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpBlockFactsService() - { - } -} +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpBlockFactsService() : CSharpBlockFacts, IBlockFactsService; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.Rewriter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.Rewriter.cs index 4db25ff8b4363..332951421c61a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.Rewriter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.Rewriter.cs @@ -189,7 +189,7 @@ public override SyntaxNode VisitCompilationUnit(CompilationUnitSyntax node) resultCompilationUnit = resultCompilationUnit.ReplaceToken(firstToken, newFirstToken); } - return resultCompilationUnit; + return resultCompilationUnit.WithAdditionalAnnotations(s_annotation); } public override SyntaxNode VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node) @@ -241,7 +241,7 @@ private SyntaxNode VisitBaseNamespaceDeclaration( resultNamespace = resultNamespace.ReplaceToken(firstToken, newFirstToken); } - return resultNamespace; + return resultNamespace.WithAdditionalAnnotations(s_annotation); } public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs index f014b6b6ce952..0d4435cf001cb 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs @@ -3,17 +3,16 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.RemoveUnnecessaryImports; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -22,14 +21,12 @@ namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryImports; [ExportLanguageService(typeof(IRemoveUnnecessaryImportsService), LanguageNames.CSharp), Shared] -internal partial class CSharpRemoveUnnecessaryImportsService : +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal partial class CSharpRemoveUnnecessaryImportsService() : AbstractRemoveUnnecessaryImportsService { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpRemoveUnnecessaryImportsService() - { - } + private static readonly SyntaxAnnotation s_annotation = new(); private static ISyntaxFormatting SyntaxFormatting => CSharpSyntaxFormatting.Instance; @@ -54,13 +51,22 @@ public override async Task RemoveUnnecessaryImportsAsync( var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var oldRoot = (CompilationUnitSyntax)root; - var newRoot = (CompilationUnitSyntax)new Rewriter(unnecessaryImports, cancellationToken).Visit(oldRoot); + var newRoot = new Rewriter(unnecessaryImports, cancellationToken).Visit(root); cancellationToken.ThrowIfCancellationRequested(); - var spansToFormat = new List(); - AddFormattingSpans(newRoot, spansToFormat, cancellationToken); + using var _ = ArrayBuilder.GetInstance(out var spansToFormat); + foreach (var node in newRoot.GetAnnotatedNodes(s_annotation)) + { + if (node is CompilationUnitSyntax { Members: [var firstMemberA, ..] }) + { + spansToFormat.Add(TextSpan.FromBounds(0, firstMemberA.SpanStart)); + } + else if (node is BaseNamespaceDeclarationSyntax { Members: [var firstMemberB, ..] } baseNamespace) + { + spansToFormat.Add(TextSpan.FromBounds(baseNamespace.Name.Span.End, firstMemberB.SpanStart)); + } + } var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(SyntaxFormatting, cancellationToken).ConfigureAwait(false); var formattedRoot = SyntaxFormatting.GetFormattingResult(newRoot, spansToFormat, formattingOptions, rules: default, cancellationToken).GetFormattedRoot(cancellationToken); @@ -68,31 +74,4 @@ public override async Task RemoveUnnecessaryImportsAsync( return document.WithSyntaxRoot(formattedRoot); } } - - private static void AddFormattingSpans( - CompilationUnitSyntax compilationUnit, - List spans, - CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - spans.Add(TextSpan.FromBounds(0, GetEndPosition(compilationUnit, compilationUnit.Members))); - - foreach (var @namespace in compilationUnit.Members.OfType()) - AddFormattingSpans(@namespace, spans, cancellationToken); - } - - private static void AddFormattingSpans( - BaseNamespaceDeclarationSyntax namespaceMember, - List spans, - CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - spans.Add(TextSpan.FromBounds(namespaceMember.SpanStart, GetEndPosition(namespaceMember, namespaceMember.Members))); - - foreach (var @namespace in namespaceMember.Members.OfType()) - AddFormattingSpans(@namespace, spans, cancellationToken); - } - - private static int GetEndPosition(SyntaxNode container, SyntaxList list) - => list.Count > 0 ? list[0].SpanStart : container.Span.End; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpReplaceDiscardDeclarationsWithAssignmentsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpReplaceDiscardDeclarationsWithAssignmentsService.cs index 6de1859a1714d..5b4c5b3e3ebde 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpReplaceDiscardDeclarationsWithAssignmentsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpReplaceDiscardDeclarationsWithAssignmentsService.cs @@ -49,6 +49,12 @@ public async Task ReplaceAsync( case LocalDeclarationStatementSyntax localDeclarationStatement: if (localDeclarationStatement.Declaration.Variables.Any(IsDiscardDeclaration)) { + // Skip replacing discard declarations in "using var" + if (localDeclarationStatement.UsingKeyword != default) + { + continue; + } + RemoveDiscardHelper.ProcessDeclarationStatement(localDeclarationStatement, editor); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs index 3d1893fcdf224..993aaf2805553 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration; @@ -132,20 +133,48 @@ public override SyntaxNode InterpolationFormatClause(string format) public override SyntaxNode TypeParameterList(IEnumerable typeParameterNames) => SyntaxFactory.TypeParameterList([.. typeParameterNames.Select(SyntaxFactory.TypeParameter)]); - internal static SyntaxTokenList GetParameterModifiers(RefKind refKind, bool forFunctionPointerReturnParameter = false) - => refKind switch + internal static SyntaxTokenList GetParameterModifiers( + IParameterSymbol parameter, bool forFunctionPointerReturnParameter = false) + => GetParameterModifiers(ParameterIsScoped(parameter), parameter.RefKind, forFunctionPointerReturnParameter); + + internal static SyntaxTokenList GetParameterModifiers( + bool isScoped, RefKind refKind, bool forFunctionPointerReturnParameter = false) + { + using var _ = ArrayBuilder.GetInstance(out var result); + + if (isScoped) + result.Add(ScopedKeyword); + + switch (refKind) { - RefKind.None => [], - RefKind.Out => [OutKeyword], - RefKind.Ref => [RefKeyword], + case RefKind.Out: + result.Add(OutKeyword); + break; + + case RefKind.Ref: + result.Add(RefKeyword); + break; + // Note: RefKind.RefReadonly == RefKind.In. Function Pointers must use the correct // ref kind syntax when generating for the return parameter vs other parameters. // The return parameter must use ref readonly, like regular methods. - RefKind.In when !forFunctionPointerReturnParameter => [InKeyword], - RefKind.RefReadOnly when forFunctionPointerReturnParameter => [RefKeyword, ReadOnlyKeyword], - RefKind.RefReadOnlyParameter => [RefKeyword, ReadOnlyKeyword], - _ => throw ExceptionUtilities.UnexpectedValue(refKind), - }; + case RefKind.In when !forFunctionPointerReturnParameter: + result.Add(InKeyword); + break; + + case RefKind.RefReadOnly when forFunctionPointerReturnParameter: + result.Add(RefKeyword); + result.Add(ReadOnlyKeyword); + break; + + case RefKind.RefReadOnlyParameter: + result.Add(RefKeyword); + result.Add(ReadOnlyKeyword); + break; + } + + return SyntaxFactory.TokenList(result); + } public override SyntaxNode Type(ITypeSymbol typeSymbol, bool typeContext) => typeContext ? typeSymbol.GenerateTypeSyntax() : typeSymbol.GenerateExpressionSyntax(); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs index 4ca0057a0fea0..610fafd8b76b1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs @@ -14,24 +14,18 @@ namespace Microsoft.CodeAnalysis.CodeGeneration; -internal abstract class CodeGenerationTypeSymbol : CodeGenerationNamespaceOrTypeSymbol, ITypeSymbol +internal abstract class CodeGenerationTypeSymbol( + IAssemblySymbol containingAssembly, + INamedTypeSymbol containingType, + ImmutableArray attributes, + Accessibility declaredAccessibility, + DeclarationModifiers modifiers, + string name, + SpecialType specialType, + NullableAnnotation nullableAnnotation) + : CodeGenerationNamespaceOrTypeSymbol(containingAssembly, containingType, attributes, declaredAccessibility, modifiers, name), ITypeSymbol { - public SpecialType SpecialType { get; protected set; } - - protected CodeGenerationTypeSymbol( - IAssemblySymbol containingAssembly, - INamedTypeSymbol containingType, - ImmutableArray attributes, - Accessibility declaredAccessibility, - DeclarationModifiers modifiers, - string name, - SpecialType specialType, - NullableAnnotation nullableAnnotation) - : base(containingAssembly, containingType, attributes, declaredAccessibility, modifiers, name) - { - this.SpecialType = specialType; - this.NullableAnnotation = nullableAnnotation; - } + public SpecialType SpecialType { get; protected set; } = specialType; public abstract TypeKind TypeKind { get; } @@ -77,7 +71,7 @@ public ImmutableArray ToMinimalDisplayParts(SemanticModel sem public override bool IsType => true; - bool ITypeSymbol.IsRefLikeType => throw new System.NotImplementedException(); + bool ITypeSymbol.IsRefLikeType => false; bool ITypeSymbol.IsUnmanagedType => throw new System.NotImplementedException(); @@ -85,17 +79,10 @@ public ImmutableArray ToMinimalDisplayParts(SemanticModel sem public virtual bool IsRecord => false; - public NullableAnnotation NullableAnnotation { get; } + public NullableAnnotation NullableAnnotation { get; } = nullableAnnotation; public ITypeSymbol WithNullableAnnotation(NullableAnnotation nullableAnnotation) - { - if (this.NullableAnnotation == nullableAnnotation) - { - return this; - } - - return CloneWithNullableAnnotation(nullableAnnotation); - } + => this.NullableAnnotation == nullableAnnotation ? this : CloneWithNullableAnnotation(nullableAnnotation); protected sealed override CodeGenerationSymbol Clone() => CloneWithNullableAnnotation(this.NullableAnnotation); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs index ee1453718264f..6bde62c6ead96 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs @@ -254,6 +254,9 @@ public bool IsInExpressionTree(SemanticModel semanticModel, SyntaxNode node, INa public string GenerateNameForExpression(SemanticModel semanticModel, SyntaxNode expression, bool capitalize, CancellationToken cancellationToken) => SemanticFacts.GenerateNameForExpression(semanticModel, expression, capitalize, cancellationToken); + public IPreprocessingSymbol GetPreprocessingSymbol(SemanticModel semanticModel, SyntaxNode node) + => SemanticFacts.GetPreprocessingSymbol(semanticModel, node); + #if !CODE_STYLE public Task GetInterceptorSymbolAsync(Document document, int position, CancellationToken cancellationToken) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs index b87a79832bdf4..5ab8c6e63bc72 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs @@ -102,6 +102,10 @@ public SyntaxNode LocalDeclarationStatement(SyntaxToken name, SyntaxNode initial public abstract SyntaxNode IsNotTypeExpression(SyntaxNode expression, SyntaxNode type); + internal static bool ParameterIsScoped(IParameterSymbol symbol) + => symbol is { RefKind: RefKind.Ref or RefKind.In or RefKind.RefReadOnlyParameter, ScopedKind: ScopedKind.ScopedRef } + or { RefKind: RefKind.None, Type.IsRefLikeType: true, ScopedKind: ScopedKind.ScopedValue }; + #region Patterns public abstract bool SupportsPatterns(ParseOptions options); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicBlockFactsService.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicBlockFactsService.vb index 296b52ba091cc..92ba91037f8f5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicBlockFactsService.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicBlockFactsService.vb @@ -9,7 +9,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.LanguageService Namespace Microsoft.CodeAnalysis.VisualBasic - Friend Class VisualBasicBlockFactsService + Friend NotInheritable Class VisualBasicBlockFactsService Inherits VisualBasicBlockFacts Implements IBlockFactsService