File: DocumentationComments\ParameterTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Symbol\Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests)
// 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.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
using SymbolExtensions = Microsoft.CodeAnalysis.Test.Utilities.SymbolExtensions;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public class ParameterTests : CSharpTestBase
    {
        #region Basic cases
 
        [Fact]
        public void ClassTypeParameter()
        {
            var source = @"
/// <typeparam name=""T""/>
/// <typeparamref name=""T""/>
class C<T>
{
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
            var nameSyntaxes = GetNameAttributeValues(compilation);
            Assert.Equal(2, nameSyntaxes.Count());
 
            var type = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
            var typeParameter = type.TypeParameters.Single().ISymbol;
 
            Assert.Equal(typeParameter, model.GetSymbolInfo(nameSyntaxes.ElementAt(0)).Symbol);
            Assert.Equal(typeParameter, model.GetSymbolInfo(nameSyntaxes.ElementAt(1)).Symbol);
        }
 
        [Fact]
        public void MethodParameter()
        {
            var source = @"
class C
{
    /// <param name=""x""/>
    /// <paramref name=""x""/>
    void M(int x) { }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
            var nameSyntaxes = GetNameAttributeValues(compilation);
            Assert.Equal(2, nameSyntaxes.Count());
 
            var type = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
            var method = type.GetMember<MethodSymbol>("M");
            var parameter = method.Parameters.Single().ISymbol;
 
            Assert.Equal(parameter, model.GetSymbolInfo(nameSyntaxes.ElementAt(0)).Symbol);
            Assert.Equal(parameter, model.GetSymbolInfo(nameSyntaxes.ElementAt(1)).Symbol);
        }
 
        [Fact]
        public void MethodTypeParameter()
        {
            var source = @"
class C
{
    /// <typeparam name=""T""/>
    /// <typeparamref name=""T""/>
    void M<T>(int x) { }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
            var nameSyntaxes = GetNameAttributeValues(compilation);
            Assert.Equal(2, nameSyntaxes.Count());
 
            var type = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
            var method = type.GetMember<MethodSymbol>("M");
            var typeParameter = method.TypeParameters.Single().ISymbol;
 
            Assert.Equal(typeParameter, model.GetSymbolInfo(nameSyntaxes.ElementAt(0)).Symbol);
            Assert.Equal(typeParameter, model.GetSymbolInfo(nameSyntaxes.ElementAt(1)).Symbol);
        }
 
        [Fact]
        public void IndexerParameter()
        {
            var source = @"
class C
{
    /// <param name=""x""/>
    /// <paramref name=""x""/>
    int this[int x] { get { return 0; } set { } }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
            var nameSyntaxes = GetNameAttributeValues(compilation);
            Assert.Equal(2, nameSyntaxes.Count());
 
            var type = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
            var indexer = type.Indexers.Single();
            var parameter = indexer.Parameters.Single().ISymbol;
 
            // NOTE: indexer parameter, not accessor parameter.
            Assert.Equal(parameter, model.GetSymbolInfo(nameSyntaxes.ElementAt(0)).Symbol);
            Assert.Equal(parameter, model.GetSymbolInfo(nameSyntaxes.ElementAt(1)).Symbol);
        }
 
        #endregion Basic cases
 
        #region Accessor value parameter
 
        [Fact]
        public void PropertyValueParameter()
        {
            var source = @"
class C
{
    /// <param name=""value""/>
    /// <paramref name=""value""/>
    int P { get; set; }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
            var nameSyntaxes = GetNameAttributeValues(compilation);
            Assert.Equal(2, nameSyntaxes.Count());
 
            var type = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
            var property = type.GetMember<PropertySymbol>("P");
            var parameter = property.SetMethod.Parameters.Single().ISymbol;
 
            // NOTE: indexer parameter, not accessor parameter.
            Assert.Equal(parameter, model.GetSymbolInfo(nameSyntaxes.ElementAt(0)).Symbol);
            Assert.Equal(parameter, model.GetSymbolInfo(nameSyntaxes.ElementAt(1)).Symbol);
        }
 
        [Fact]
        public void IndexerValueParameter()
        {
            var source = @"
class C
{
    /// <param name=""value""/>
    /// <paramref name=""value""/>
    int this[int x] { get { return 0; } set { } }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
            var nameSyntaxes = GetNameAttributeValues(compilation);
            Assert.Equal(2, nameSyntaxes.Count());
 
            var type = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
            var indexer = type.Indexers.Single();
            var parameter = indexer.SetMethod.Parameters.Last().ISymbol;
 
            // NOTE: accessor parameter - there is no corresponding indexer parameter.
            Assert.Equal(parameter, model.GetSymbolInfo(nameSyntaxes.ElementAt(0)).Symbol);
            Assert.Equal(parameter, model.GetSymbolInfo(nameSyntaxes.ElementAt(1)).Symbol);
        }
 
        [Fact]
        public void CustomEventValueParameter()
        {
            var source = @"
class C
{
    /// <param name=""value""/>
    /// <paramref name=""value""/>
    event System.Action E { add { } remove { } };
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
            var nameSyntaxes = GetNameAttributeValues(compilation);
            Assert.Equal(2, nameSyntaxes.Count());
 
            // As in dev11, this is not supported.
            Assert.Equal(SymbolInfo.None, model.GetSymbolInfo(nameSyntaxes.ElementAt(0)));
            Assert.Equal(SymbolInfo.None, model.GetSymbolInfo(nameSyntaxes.ElementAt(1)));
        }
 
        [Fact]
        public void FieldLikeEventValueParameter()
        {
            var source = @"
class C
{
    /// <param name=""value""/>
    /// <paramref name=""value""/>
    event System.Action E;
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
            var nameSyntaxes = GetNameAttributeValues(compilation);
            Assert.Equal(2, nameSyntaxes.Count());
 
            // As in dev11, this is not supported.
            Assert.Equal(SymbolInfo.None, model.GetSymbolInfo(nameSyntaxes.ElementAt(0)));
            Assert.Equal(SymbolInfo.None, model.GetSymbolInfo(nameSyntaxes.ElementAt(1)));
        }
 
        [Fact]
        public void ReadonlyPropertyValueParameter()
        {
            var source = @"
class C
{
    /// <param name=""value""/>
    /// <paramref name=""value""/>
    int P { get { return 0; } }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
            var nameSyntaxes = GetNameAttributeValues(compilation);
            Assert.Equal(2, nameSyntaxes.Count());
 
            // BREAK: Dev11 supports this, but we don't have a symbol.
            Assert.Equal(SymbolInfo.None, model.GetSymbolInfo(nameSyntaxes.ElementAt(0)));
            Assert.Equal(SymbolInfo.None, model.GetSymbolInfo(nameSyntaxes.ElementAt(1)));
        }
 
        [Fact]
        public void ReadonlyIndexerValueParameter()
        {
            var source = @"
class C
{
    /// <param name=""value""/>
    /// <paramref name=""value""/>
    int this[int x] { get { return 0; } }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
            var nameSyntaxes = GetNameAttributeValues(compilation);
            Assert.Equal(2, nameSyntaxes.Count());
 
            // BREAK: Dev11 supports this, but we don't have a symbol.
            Assert.Equal(SymbolInfo.None, model.GetSymbolInfo(nameSyntaxes.ElementAt(0)));
            Assert.Equal(SymbolInfo.None, model.GetSymbolInfo(nameSyntaxes.ElementAt(1)));
        }
 
        #endregion Accessor value parameter
 
        #region Complex parameter names
 
        [Fact]
        public void VerbatimKeyword()
        {
            var source = @"
class C
{
    /// <param name=""int""/>
    /// <param name=""@int""/>
    void M(int @int) { }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
            var nameSyntaxes = GetNameAttributeValues(compilation);
            Assert.Equal(2, nameSyntaxes.Count());
 
            var type = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
            var method = type.GetMember<MethodSymbol>("M");
            var parameter = method.Parameters.Single().ISymbol;
 
            // NOTE: "@" is neither required nor supported in name attributes.
            Assert.Equal(parameter, model.GetSymbolInfo(nameSyntaxes.ElementAt(0)).Symbol);
            Assert.Equal(SymbolInfo.None, model.GetSymbolInfo(nameSyntaxes.ElementAt(1)));
        }
 
        [Fact]
        public void UnicodeEscape()
        {
            var source = @"
class C
{
    /// <param name=""a""/>
    /// <param name=""\u0062""/>
    /// <param name=""\u0063""/>
    void M(int \u0061, int b, int \u0063) { }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
            var nameSyntaxes = GetNameAttributeValues(compilation);
            Assert.Equal(3, nameSyntaxes.Count());
 
            var type = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
            var method = type.GetMember<MethodSymbol>("M");
            var parameters = method.Parameters.GetPublicSymbols();
 
            Assert.Equal(parameters.ElementAt(0), model.GetSymbolInfo(nameSyntaxes.ElementAt(0)).Symbol);
            Assert.Equal(parameters.ElementAt(1), model.GetSymbolInfo(nameSyntaxes.ElementAt(1)).Symbol);
            Assert.Equal(parameters.ElementAt(2), model.GetSymbolInfo(nameSyntaxes.ElementAt(2)).Symbol);
        }
 
        #endregion Complex parameter names
 
        #region Ambiguities
 
        [Fact]
        public void AmbiguousParameter()
        {
            var source = @"
class C
{
    /// <param name=""a""/>
    void M(int a, int a) { }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
            var nameSyntax = GetNameAttributeValues(compilation).Single();
 
            var type = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
            var method = type.GetMember<MethodSymbol>("M");
            var parameters = method.Parameters.GetPublicSymbols();
 
            var info = model.GetSymbolInfo(nameSyntax);
            Assert.Equal(CandidateReason.Ambiguous, info.CandidateReason);
            AssertEx.SetEqual(parameters, info.CandidateSymbols);
        }
 
        [Fact]
        public void AmbiguousTypeParameter()
        {
            var source = @"
class C
{
    /// <typeparam name=""T""/>
    void M<T, T>() { }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
            var nameSyntax = GetNameAttributeValues(compilation).Single();
 
            var type = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
            var method = type.GetMember<MethodSymbol>("M");
            var typeParameters = method.TypeParameters.GetPublicSymbols();
 
            var info = model.GetSymbolInfo(nameSyntax);
            Assert.Equal(CandidateReason.Ambiguous, info.CandidateReason);
            AssertEx.SetEqual(typeParameters, info.CandidateSymbols);
        }
 
        [Fact]
        public void AmbiguousParameterAndTypeParameter()
        {
            var source = @"
class C
{
    /// <typeparam name=""T""/>
    /// <param name=""T""/>
    void M<T>(int T) { }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
            var nameSyntaxes = GetNameAttributeValues(compilation);
            Assert.Equal(2, nameSyntaxes.Count());
 
            var type = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
            var method = type.GetMember<MethodSymbol>("M");
            var typeParameter = method.TypeParameters.Single().ISymbol;
            var parameter = method.Parameters.Single().ISymbol;
 
            // No problem because the context determines which are visible.
            Assert.Equal(typeParameter, model.GetSymbolInfo(nameSyntaxes.ElementAt(0)).Symbol);
            Assert.Equal(parameter, model.GetSymbolInfo(nameSyntaxes.ElementAt(1)).Symbol);
        }
 
        #endregion Ambiguities
 
        #region Lookup
 
        [Fact]
        public void ClassLookup()
        {
            var source = @"
/// <param name=""pos1""/>
/// <paramref name=""pos2""/>
/// <typeparam name=""pos3""/>
/// <typeparamref name=""pos4""/>
class C<T>
{
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
 
            int pos1 = source.IndexOf("pos1", StringComparison.Ordinal);
            int pos2 = source.IndexOf("pos2", StringComparison.Ordinal);
            int pos3 = source.IndexOf("pos3", StringComparison.Ordinal);
            int pos4 = source.IndexOf("pos4", StringComparison.Ordinal);
 
            AssertEx.SetEqual(model.LookupSymbols(pos1).Select(SymbolExtensions.ToTestDisplayString));
            AssertEx.SetEqual(model.LookupSymbols(pos2).Select(SymbolExtensions.ToTestDisplayString));
            AssertEx.SetEqual(model.LookupSymbols(pos3).Select(SymbolExtensions.ToTestDisplayString), "T");
            AssertEx.SetEqual(model.LookupSymbols(pos4).Select(SymbolExtensions.ToTestDisplayString), "T");
        }
 
        [Fact]
        public void MethodLookup()
        {
            var source = @"
class C
{
    /// <param name=""pos1""/>
    /// <paramref name=""pos2""/>
    /// <typeparam name=""pos3""/>
    /// <typeparamref name=""pos4""/>
    void M<T>(int x) { }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
 
            int pos1 = source.IndexOf("pos1", StringComparison.Ordinal);
            int pos2 = source.IndexOf("pos2", StringComparison.Ordinal);
            int pos3 = source.IndexOf("pos3", StringComparison.Ordinal);
            int pos4 = source.IndexOf("pos4", StringComparison.Ordinal);
 
            AssertEx.SetEqual(model.LookupSymbols(pos1).Select(SymbolExtensions.ToTestDisplayString), "System.Int32 x");
            AssertEx.SetEqual(model.LookupSymbols(pos2).Select(SymbolExtensions.ToTestDisplayString), "System.Int32 x");
            AssertEx.SetEqual(model.LookupSymbols(pos3).Select(SymbolExtensions.ToTestDisplayString), "T");
            AssertEx.SetEqual(model.LookupSymbols(pos4).Select(SymbolExtensions.ToTestDisplayString), "T");
        }
 
        [Fact]
        public void PropertyLookup()
        {
            var source = @"
class C
{
    /// <param name=""pos1""/>
    /// <paramref name=""pos2""/>
    /// <typeparam name=""pos3""/>
    /// <typeparamref name=""pos4""/>
    int P { get; set; }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
 
            int pos1 = source.IndexOf("pos1", StringComparison.Ordinal);
            int pos2 = source.IndexOf("pos2", StringComparison.Ordinal);
            int pos3 = source.IndexOf("pos3", StringComparison.Ordinal);
            int pos4 = source.IndexOf("pos4", StringComparison.Ordinal);
 
            AssertEx.SetEqual(model.LookupSymbols(pos1).Select(SymbolExtensions.ToTestDisplayString), "System.Int32 value");
            AssertEx.SetEqual(model.LookupSymbols(pos2).Select(SymbolExtensions.ToTestDisplayString), "System.Int32 value");
            AssertEx.SetEqual(model.LookupSymbols(pos3).Select(SymbolExtensions.ToTestDisplayString));
            AssertEx.SetEqual(model.LookupSymbols(pos4).Select(SymbolExtensions.ToTestDisplayString));
        }
 
        [Fact]
        public void ReadonlyPropertyLookup()
        {
            var source = @"
class C
{
    /// <param name=""pos1""/>
    /// <paramref name=""pos2""/>
    /// <typeparam name=""pos3""/>
    /// <typeparamref name=""pos4""/>
    int P { get { return 0; } }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
 
            int pos1 = source.IndexOf("pos1", StringComparison.Ordinal);
            int pos2 = source.IndexOf("pos2", StringComparison.Ordinal);
            int pos3 = source.IndexOf("pos3", StringComparison.Ordinal);
            int pos4 = source.IndexOf("pos4", StringComparison.Ordinal);
 
            AssertEx.SetEqual(model.LookupSymbols(pos1).Select(SymbolExtensions.ToTestDisplayString));
            AssertEx.SetEqual(model.LookupSymbols(pos2).Select(SymbolExtensions.ToTestDisplayString));
            AssertEx.SetEqual(model.LookupSymbols(pos3).Select(SymbolExtensions.ToTestDisplayString));
            AssertEx.SetEqual(model.LookupSymbols(pos4).Select(SymbolExtensions.ToTestDisplayString));
        }
 
        [Fact]
        public void IndexerLookup()
        {
            var source = @"
class C
{
    /// <param name=""pos1""/>
    /// <paramref name=""pos2""/>
    /// <typeparam name=""pos3""/>
    /// <typeparamref name=""pos4""/>
    int this[int x] { get { return 0; } set { } }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
 
            int pos1 = source.IndexOf("pos1", StringComparison.Ordinal);
            int pos2 = source.IndexOf("pos2", StringComparison.Ordinal);
            int pos3 = source.IndexOf("pos3", StringComparison.Ordinal);
            int pos4 = source.IndexOf("pos4", StringComparison.Ordinal);
 
            AssertEx.SetEqual(model.LookupSymbols(pos1).Select(SymbolExtensions.ToTestDisplayString), "System.Int32 x", "System.Int32 value");
            AssertEx.SetEqual(model.LookupSymbols(pos2).Select(SymbolExtensions.ToTestDisplayString), "System.Int32 x", "System.Int32 value");
            AssertEx.SetEqual(model.LookupSymbols(pos3).Select(SymbolExtensions.ToTestDisplayString));
            AssertEx.SetEqual(model.LookupSymbols(pos4).Select(SymbolExtensions.ToTestDisplayString));
        }
 
        [Fact]
        public void ReadonlyIndexerLookup()
        {
            var source = @"
class C
{
    /// <param name=""pos1""/>
    /// <paramref name=""pos2""/>
    /// <typeparam name=""pos3""/>
    /// <typeparamref name=""pos4""/>
    int this[int x] { get { return 0; } }
}
";
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
 
            int pos1 = source.IndexOf("pos1", StringComparison.Ordinal);
            int pos2 = source.IndexOf("pos2", StringComparison.Ordinal);
            int pos3 = source.IndexOf("pos3", StringComparison.Ordinal);
            int pos4 = source.IndexOf("pos4", StringComparison.Ordinal);
 
            AssertEx.SetEqual(model.LookupSymbols(pos1).Select(SymbolExtensions.ToTestDisplayString), "System.Int32 x");
            AssertEx.SetEqual(model.LookupSymbols(pos2).Select(SymbolExtensions.ToTestDisplayString), "System.Int32 x");
            AssertEx.SetEqual(model.LookupSymbols(pos3).Select(SymbolExtensions.ToTestDisplayString));
            AssertEx.SetEqual(model.LookupSymbols(pos4).Select(SymbolExtensions.ToTestDisplayString));
        }
 
        [Fact]
        public void CustomEventLookup()
        {
            var source = @"
class C<T>
{
    /// <param name=""pos1""/>
    /// <paramref name=""pos2""/>
    /// <typeparam name=""pos3""/>
    /// <typeparamref name=""pos4""/>
    event System.Action E { add { } remove { } }
}
";
            var compilation = (Compilation)CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
 
            int pos1 = source.IndexOf("pos1", StringComparison.Ordinal);
            int pos2 = source.IndexOf("pos2", StringComparison.Ordinal);
            int pos3 = source.IndexOf("pos3", StringComparison.Ordinal);
            int pos4 = source.IndexOf("pos4", StringComparison.Ordinal);
 
            // As in Dev11, we do not consider the value parameter.
            AssertEx.SetEqual(model.LookupSymbols(pos1).Select(SymbolExtensions.ToTestDisplayString));
            AssertEx.SetEqual(model.LookupSymbols(pos2).Select(SymbolExtensions.ToTestDisplayString));
            AssertEx.SetEqual(model.LookupSymbols(pos3).Select(SymbolExtensions.ToTestDisplayString));
            AssertEx.SetEqual(model.LookupSymbols(pos4), compilation.GlobalNamespace.GetMember<INamedTypeSymbol>("C").TypeParameters.Single());
        }
 
        [Fact]
        public void FieldLikeEventLookup()
        {
            var source = @"
class C<T>
{
    /// <param name=""pos1""/>
    /// <paramref name=""pos2""/>
    /// <typeparam name=""pos3""/>
    /// <typeparamref name=""pos4""/>
    event System.Action E;
}
";
            var compilation = (Compilation)CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
 
            int pos1 = source.IndexOf("pos1", StringComparison.Ordinal);
            int pos2 = source.IndexOf("pos2", StringComparison.Ordinal);
            int pos3 = source.IndexOf("pos3", StringComparison.Ordinal);
            int pos4 = source.IndexOf("pos4", StringComparison.Ordinal);
 
            // As in Dev11, we do not consider the value parameter.
            AssertEx.SetEqual(model.LookupSymbols(pos1).Select(SymbolExtensions.ToTestDisplayString));
            AssertEx.SetEqual(model.LookupSymbols(pos2).Select(SymbolExtensions.ToTestDisplayString));
            AssertEx.SetEqual(model.LookupSymbols(pos3).Select(SymbolExtensions.ToTestDisplayString));
            AssertEx.SetEqual(model.LookupSymbols(pos4), compilation.GlobalNamespace.GetMember<INamedTypeSymbol>("C").TypeParameters.Single());
        }
 
        #endregion Lookup
 
        [Fact]
        public void CrefAttributeNameCaseMismatch()
        {
            var source = @"
class C
{
    /// <Param name=""x"">Fine - case of element name doesn't matter.</Param>
    /// <param Name=""y"">Doesn't count - attribute name must be lowercase.</param>
    void M(int x, int y) { }
}
";
 
            // Element names don't have to be lowercase, but "name" does.
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            compilation.VerifyDiagnostics(
                // (6,23): warning CS1573: Parameter 'y' has no matching param tag in the XML comment for 'C.M(int, int)' (but other parameters do)
                //     void M(int x, int y) { }
                Diagnostic(ErrorCode.WRN_MissingParamTag, "y").WithArguments("y", "C.M(int, int)"));
            Assert.Equal(1, GetNameAttributeValues(compilation).Count());
        }
 
        [Fact]
        public void ContainingSymbol()
        {
            var source = @"
class C
{
    /// <param name=""x"">Comment.</param>
    void M(int x) { }
}
";
 
            var compilation = (Compilation)CreateCompilationWithMscorlib40AndDocumentationComments(source);
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
 
            var type = compilation.GlobalNamespace.GetMember<INamedTypeSymbol>("C");
 
            int start = source.IndexOf("param", StringComparison.Ordinal);
            int end = source.LastIndexOf("param", StringComparison.Ordinal);
            for (int position = start; position < end; position++)
            {
                Assert.Equal(type, model.GetEnclosingSymbol(position));
            }
        }
 
        [WorkItem(531161, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/531161")]
        [Fact]
        public void AttributeNameHasPrefix()
        {
            var source = @"
class Program
{
    /// <param xmlns:name=""Invalid""/>
    void M() { } 
}
";
 
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            compilation.VerifyDiagnostics();
            Assert.Equal(0, GetNameAttributeValues(compilation).Count());
        }
 
        [WorkItem(531160, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/531160")]
        [Fact]
        public void DuplicateAttribute()
        {
            var source = @"
class Program
{
    /// <param name=""x"" name=""y""/>
    void M(int x, int y) { } 
}
";
 
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            compilation.VerifyDiagnostics(
                // (4,24): warning CS1570: XML comment has badly formed XML -- 'Duplicate 'name' attribute'
                //     /// <param name="x" name="y"/>
                Diagnostic(ErrorCode.WRN_XMLParseError, @" name=""y").WithArguments("name"));
 
            var model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single());
            var nameSyntaxes = GetNameAttributeValues(compilation).ToArray();
 
            var method = compilation.GlobalNamespace.GetMember<NamedTypeSymbol>("Program").GetMember<MethodSymbol>("M").GetPublicSymbol();
 
            Assert.Equal(method.Parameters[0], model.GetSymbolInfo(nameSyntaxes[0]).Symbol);
            Assert.Equal(method.Parameters[1], model.GetSymbolInfo(nameSyntaxes[1]).Symbol);
        }
 
        [WorkItem(531233, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/531233")]
        [Fact]
        public void NameInOtherElement()
        {
            var source = @"
class C
{
    /// <other name=""C""/>
    void M() { }
}
";
 
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            compilation.VerifyDiagnostics();
 
            Assert.Equal(0, GetNameAttributeValues(compilation).Count());
        }
 
        [WorkItem(531337, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/531337")]
        [Fact]
        public void NamesInMethodBody()
        {
            var source = @"
class C
{
    void M<T>(T t)
    {
        /// <param name=""t""/>
        /// <paramref name=""t""/>
        /// <typeparam name=""T""/>
        /// <typeparamref name=""T""/>
    }
}
";
 
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            compilation.VerifyDiagnostics(
                // (6,9): warning CS1587: XML comment is not placed on a valid language element
                //         /// <param name=""t""/>
                Diagnostic(ErrorCode.WRN_UnprocessedXMLComment, "/"));
 
            var tree = compilation.SyntaxTrees.Single();
            var names = GetNameAttributeValues(compilation).ToArray();
            var model = compilation.GetSemanticModel(tree);
 
            Assert.Null(model.GetSymbolInfo(names[0]).Symbol);
            Assert.Null(model.GetSymbolInfo(names[1]).Symbol);
            Assert.Null(model.GetSymbolInfo(names[2]).Symbol);
            Assert.Null(model.GetSymbolInfo(names[3]).Symbol);
        }
 
        [WorkItem(531337, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/531337")]
        [Fact]
        public void NamesOnAccessor()
        {
            var source = @"
class C<T>
{
    int this[T t]
    {
        /// <typeparam name=""T""/>
        /// <typeparamref name=""T""/>
        /// <param name=""t""/>
        /// <paramref name=""t""/>
        /// <param name=""value""/>
        /// <paramref name=""value""/>
        get { return 0; }
 
        /// <typeparam name=""T""/>
        /// <typeparamref name=""T""/>
        /// <param name=""t""/>
        /// <paramref name=""t""/>
        /// <param name=""value""/>
        /// <paramref name=""value""/>
        set { }
    }
}
";
 
            var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(source);
            compilation.VerifyDiagnostics(
                // (6,9): warning CS1587: XML comment is not placed on a valid language element
                //         /// <typeparam name="T"/>
                Diagnostic(ErrorCode.WRN_UnprocessedXMLComment, "/"),
                // (14,9): warning CS1587: XML comment is not placed on a valid language element
                //         /// <typeparam name="T"/>
                Diagnostic(ErrorCode.WRN_UnprocessedXMLComment, "/"));
 
            var tree = compilation.SyntaxTrees.Single();
            var names = GetNameAttributeValues(compilation).ToArray();
            var model = compilation.GetSemanticModel(tree);
 
            // Getter
 
            //T
            Assert.Null(model.GetSymbolInfo(names[0]).Symbol);
            Assert.Null(model.GetSymbolInfo(names[1]).Symbol);
 
            //t
            Assert.Null(model.GetSymbolInfo(names[2]).Symbol);
            Assert.Null(model.GetSymbolInfo(names[3]).Symbol);
 
            //value
            Assert.Null(model.GetSymbolInfo(names[4]).Symbol);
            Assert.Null(model.GetSymbolInfo(names[5]).Symbol);
 
            // Setter
 
            //T
            Assert.Null(model.GetSymbolInfo(names[6]).Symbol);
            Assert.Null(model.GetSymbolInfo(names[7]).Symbol);
 
            //t
            Assert.Null(model.GetSymbolInfo(names[8]).Symbol);
            Assert.Null(model.GetSymbolInfo(names[9]).Symbol);
 
            //value
            Assert.Null(model.GetSymbolInfo(names[10]).Symbol);
            Assert.Null(model.GetSymbolInfo(names[11]).Symbol);
        }
 
        [Fact]
        public void ExtensionMethodsAreNotAvailableInEarlierCSharpVersions()
        {
            var code = @"
 public static class Test
 {
     public static void DoSomething(this int x) { }
 }";
 
            CreateCompilation(code, parseOptions: new CSharpParseOptions(LanguageVersion.CSharp2)).VerifyDiagnostics(
                // (4,37): error CS8023: Feature 'extension method' is not available in C# 2. Please use language version 3 or greater.
                //      public static void DoSomething(this int x) { }
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion2, "this").WithArguments("extension method", "3").WithLocation(4, 37));
 
            CreateCompilation(code, parseOptions: new CSharpParseOptions(LanguageVersion.Latest)).VerifyDiagnostics();
        }
 
        private static IEnumerable<IdentifierNameSyntax> GetNameAttributeValues(CSharpCompilation compilation)
        {
            return compilation.SyntaxTrees.SelectMany(tree =>
            {
                var docComments = tree.GetCompilationUnitRoot().DescendantTrivia().Select(trivia => trivia.GetStructure()).OfType<DocumentationCommentTriviaSyntax>();
                return docComments.SelectMany(docComment => docComment.DescendantNodes().OfType<XmlNameAttributeSyntax>().Select(attr => attr.Identifier));
            });
        }
    }
}