dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
19.02k stars 4.03k forks source link

IDE0039 'Use local function' breaks code #22672

Closed TessenR closed 6 years ago

TessenR commented 7 years ago

Version Used:

Microsoft Visual Studio Professional 2017 Preview (2)
Version 15.5.0 Preview 1.0
VisualStudio.15.Preview/15.5.0-pre.1.0+27009.1
Microsoft .NET Framework
Version 4.7.02046

Steps to Reproduce:

  1. Paste the following code into VS.
    
    using System;
    using System.Linq.Expressions;

namespace ConsoleApp1 { class Program { static void Main(string[] args) { new Enclosing.Class().Caller(); } }

class Enclosing where T : class { delegate T MyDelegate(T t = null); static void Callee(MyDelegate d) => d(default(T));

public class Class<T> where T : class
{
  public void Caller()
  {
    MyDelegate local = x => x;
    Console.WriteLine("");

    var doubleDelegate = local + local;
    local.Invoke();
    Console.WriteLine(nameof(local.ToString));
    Expression<Action> expression = () => local(null);
    Callee(local);
  }
}

} }

2. Convert `MyDelegate local = x => x;` to local function as suggested by IDE0039

**Expected Behavior**:
Either IDE0039 not suggested or code is compilable after such change or conflicts are shown.

**Actual Behavior**:
1. No conflicts:
![image](https://user-images.githubusercontent.com/5566206/31494299-5f6853a0-af5b-11e7-9e76-4ac575d8c541.png)
2. Code is completely broken:
```cs
using System;
using System.Linq.Expressions;

namespace ConsoleApp1
{
  class Program
  {
    static void Main(string[] args)
    {
      new Enclosing<string>.Class<Program>().Caller();
    }
  }

  class Enclosing<T> where T : class
  {
    delegate T MyDelegate(T t = null);
    static void Callee(MyDelegate d) => d(default(T));

    public class Class<T> where T : class
    {
      public void Caller()
      {
        T local(T x) => x;
        Console.WriteLine("");

        var doubleDelegate = local + local; // error CS0019
        local.Invoke();  // error CS0119
        Console.WriteLine(nameof(local.ToString)); // error CS0019
        Expression<Action> expression = () => local(null); // error CS8110
        Callee(local); // error CS1503
      }
    }
  }
}
Error CS1503 Argument 1: cannot convert from 'method group' to 'Enclosing<T>.MyDelegate'
Error CS0019 Operator '+' cannot be applied to operands of type 'method group' and 'method group'
Error CS0119 'local(T)' is a method, which is not valid in the given context
Error CS0119 'local(T)' is a method, which is not valid in the given context
Error CS8110 An expression tree may not contain a reference to a local function
CyrusNajmabadi commented 6 years ago

Cute. Let me know if you want any info on this @chborl :)

chborl commented 6 years ago

Thanks @CyrusNajmabadi! I'll take a look at this in the next couple of days.

aluanhaddad commented 6 years ago

I also have this refactoring suggested in a case where it breaks code.

Here is a minimal repro

static class FunctionExtensions
{
    public static Func<TResult> Apply<T, TResult>(this Func<T, TResult> f, T x) => () => f(x);
}
class Program
{
    static void Main(string[] args)
    {
        Func<int, int> addOne = x => x + 1; // IDE0039 suggested
        var getSix = addOne.Apply(5);
    }
}

IDE suggests replacing f with a local function which prevents it from being the receiver of the extension method Apply.

Let me know if I should open a separate issue.

Version Details: Microsoft Visual Studio Professional 2017 Preview Version 15.5.0 Preview 3.0 VisualStudio.15.Preview/15.5.0-pre.3.0+27102.0 Microsoft .NET Framework Version 4.7.02556

Visual C# 2017 00369-60000-00001-AA141 Microsoft Visual C# 2017

CyrusNajmabadi commented 6 years ago

@aluanhaddad I think it's fine to keep that case rolled into this one.

aluanhaddad commented 6 years ago

@CyrusNajmabadi thanks!