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.06k stars 4.04k forks source link

CS0021 error (Cannot apply indexing with [] to an expression of type) appears incorrectly on code that compiles #69663

Closed vsfeedback closed 1 year ago

vsfeedback commented 1 year ago

This issue has been moved from a ticket on Developer Community. [jcouv update:] Here's the internal azdo issue.


[severity:It's more difficult to complete my work] I have noticed that in some situations, the error CS0021 appears on a call to an indexer declared in a type in my own code that appears to be valid and successfully compiles.

The issue seems to be related to the fact that the type in question has a primary constructor, because removing it causes the error to disappear.

A sample project where the issue is occuring is attached (line 3 of Program.cs).

The same issue occurs when using the C# extension for Visual Studio Code.


Original Comments

Feedback Bot on 8/21/2023, 07:26 PM:

(private comment, text removed)

Feedback Bot on 8/22/2023, 06:02 AM:

(private comment, text removed)


Original Solutions

(no solutions)

CyrusNajmabadi commented 1 year ago

@dotnet/roslyn-compiler Code that fails is attached to the original azdo bug.

CyrusNajmabadi commented 1 year ago

User code:

File 1:

namespace Aiml.Tags;
internal class Program {
    public static void TestMethod(Tuple tuple) => _ = tuple[""];
}

File 2:

using System.Collections;

namespace Aiml;
/// <summary>Represents a set of variable bindings in the process of resolving a <see cref="Tags.Select"/> query.</summary>
/// <remarks>This class is represented as a singly-linked list.</remarks>
public class Tuple(string key, string value, Tuple? next) : IEnumerable<KeyValuePair<string, string>> {
    public string Key { get; } = key;
    public string Value { get; } = value;
    public Tuple? Next { get; } = next;

    public Tuple(string key, string value) : this(key, value, null) { }

    public string? this[string key] {
        get {
            var tuple = this;
            do {
                if (key.Equals(tuple.Key, StringComparison.OrdinalIgnoreCase)) return tuple.Value;
                tuple = tuple.Next;
            } while (tuple != null);
            return null;
        }
    }

    /// <summary>Returns an encoded string containing the contents of the specified variables. The encoded string shall not contain spaces.</summary>
    public string Encode(IReadOnlyCollection<string>? visibleVars) {
        using var ms = new MemoryStream();
        using var writer = new BinaryWriter(ms);
        foreach (var e in this) {
            if (visibleVars is not null && !visibleVars.Contains(e.Key)) continue;
            writer.Write(e.Key);
            writer.Write(e.Value);
        }
        return Convert.ToBase64String(ms.GetBuffer(), 0, (int) ms.Position);
    }

    /// <summary>Returns the value of the specified variable from an encoded string, or <see langword="null"/> if the variable is not bound in the encoded string.</summary>
    public static string? GetFromEncoded(string encoded, string key) {
        var array = Convert.FromBase64String(encoded);
        using var ms = new MemoryStream(array);
        using var reader = new BinaryReader(ms);
        while (ms.Position < ms.Length) {
            var key2 = reader.ReadString();
            var value = reader.ReadString();
            if (key.Equals(key2, StringComparison.OrdinalIgnoreCase)) return value;
        }
        return null;
    }

    public IEnumerator<KeyValuePair<string, string>> GetEnumerator() {
        var tuple = this;
        do {
            yield return new(tuple.Key, tuple.Value);
            tuple = tuple.Next;
        } while (tuple != null);
    }
    IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
jcouv commented 1 year ago

I was not able to repro with the given code. The tuple[""] doesn't show any error.

the error CS0021 appears on a call to an indexer declared in a type in my own code that appears to be valid and successfully compiles.

I'm a bit confused by this observation. If there is a CS0021, then the program should not "successfully compile" since there's an error. Do you mean that there's a difference between what the IDE and the Build report? If that's the case, are you referencing a compiler toolset nuget package by any chance? (that could explain the disparity)

AndrioCelos commented 1 year ago

There is indeed a difference between what the IDE and build report. No such error appears in the MSBuild output.

I've reproduced this in a sample project created using dotnet new console that doesn't explicitly reference any packages at all, so I don't see how that could be the issue.

#error version produces the following:

Compiler version: '4.8.0-1.23419.1 (5fcefbef)'. Language version: preview.Roslyn[CS8304](https://msdn.microsoft.com/query/roslyn.query?appId%3Droslyn%26k%3Dk%28CS8304%29)
jcouv commented 1 year ago

I've reproduced this in a sample project created using dotnet new console that doesn't explicitly reference any packages at all [...]

@AndrioCelos Could you share that small repro project? I'll try it again.

#error version produces the following [...]

Thanks. That's useful to narrow down why you can repro but I couldn't. In my case, VS/Intellisense shows: Compiler version: '4.8.0-1.23415.13 (00c15ab5)'.
I'll take a look to see if there are any obvious/relevant changes between those two versions.

AndrioCelos commented 1 year ago

I've reproduced this in a sample project created using dotnet new console that doesn't explicitly reference any packages at all [...]

@AndrioCelos Could you share that small repro project? I'll try it again.

#error version produces the following [...]

Thanks. That's useful to narrow down why you can repro but I couldn't. In my case, VS/Intellisense shows: Compiler version: '4.8.0-1.23415.13 (00c15ab5)'. I'll take a look to see if there are any obvious/relevant changes between those two versions.

It's available now at https://gist.github.com/AndrioCelos/1264f6a36ae9f9d96e76f7330bc8bbdc.

  1. git clone the above URL.
  2. Open the csproj with Visual Studio (reproduced with VS Community 2022 v17.7.3) or open the directory with Visual Studio Code with the C# extension (reproduced on 1.82.0-insider and extension v2.0.436).
  3. Open Program.cs and observe IntelliSense messages.

Also, as an update to my earlier comment, the version of Roslyn used in my Visual Studio setup produced this:

Error   CS8304  Compiler version: '4.7.0-3.23416.8 (43b0b05c)'. Language version: preview.
jjonescz commented 1 year ago

This test reproduces the issue:

[Fact]
public void Indexer_SymbolInfo()
{
    var source = """
        C c = null;
        _ = c[2];

        class C(int p)
        {
            public int this[int i] => p;
        }
        """;
    var comp = CreateCompilation(source);
    var tree = comp.SyntaxTrees.Single();
    var indexer = tree.GetRoot().DescendantNodes().OfType<ElementAccessExpressionSyntax>().Single();
    Assert.Equal("c[2]", indexer.ToString());
    var model = comp.GetSemanticModel(tree);
    var info = model.GetSymbolInfo(indexer);
    Assert.NotNull(info.Symbol);
}

Seems the indexer is not found due to this check which I'm not sure why it's there:

https://github.com/dotnet/roslyn/blob/cc0bd7df1e148a46ec0bb9a3138e6644e2966102/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs#L3388

This doesn't happen during normal compilation because this code is only executed if searching for the symbol when members are not yet completed.

jcouv commented 1 year ago

Thanks much @jjonescz