Intellisense not showing methods from the Base class in Signature Help#1030
Intellisense not showing methods from the Base class in Signature Help#1030DustinCampbell merged 17 commits intoOmniSharp:masterfrom
Conversation
| } | ||
|
|
||
| return symbol.ContainingType.GetMembers(symbol.Name).OfType<IMethodSymbol>(); | ||
| var MethodOverloads = symbol.ContainingType.GetMembers(symbol.Name).OfType<IMethodSymbol>(); |
There was a problem hiding this comment.
local variables should start with lowercase
| return symbol.ContainingType.GetMembers(symbol.Name).OfType<IMethodSymbol>(); | ||
| var MethodOverloads = symbol.ContainingType.GetMembers(symbol.Name).OfType<IMethodSymbol>(); | ||
| var BaseType = symbol.ContainingType.BaseType; | ||
| while(BaseType!=null && BaseType.ContainingNamespace.Name!= "System") |
There was a problem hiding this comment.
spacing is off here.
Also, perhaps it would be better to check for baseTypeSymbol.SpecialType != SpecialType.System_Object and terminate there instead rather than on "System" namespace
There was a problem hiding this comment.
The above mentioned change is failing for the case of attribute constructor as the System.Attribute class doesn't have the SpecialType as System_Object, hence for that case we will also get an Attribute.Attribute method which is not required.This is the reason the System Namespace has been used.
| return symbol.ContainingType.GetMembers(symbol.Name).OfType<IMethodSymbol>(); | ||
| var methodOverloads = symbol.ContainingType.GetMembers(symbol.Name).OfType<IMethodSymbol>(); | ||
| var baseTypeSymbol = symbol.ContainingType.BaseType; | ||
| while(baseTypeSymbol != null && baseTypeSymbol.ContainingNamespace.Name != "System") |
There was a problem hiding this comment.
It looks like Roslyn has a public "GetMemberGroup" extension method you can call that might do all of this for you. Should we just call that? (http://source.roslyn.io/#Microsoft.CodeAnalysis.CSharp.Features/SignatureHelp/InvocationExpressionSignatureHelpProvider.cs,71)
| var baseTypeSymbol = symbol.ContainingType.BaseType; | ||
| while(baseTypeSymbol != null && baseTypeSymbol.ContainingNamespace.Name != "System") | ||
| { | ||
| methodOverloads = methodOverloads.Concat(baseTypeSymbol.GetMembers(symbol.Name).OfType<IMethodSymbol>()); |
There was a problem hiding this comment.
I think this will add inaccessible members with the same name.
There was a problem hiding this comment.
Yes it is returning all the methods regardless of their accessibility.
| } | ||
|
|
||
| [Fact] | ||
| public async Task SignatureHelpForOverloadedMethodsInheritance() |
There was a problem hiding this comment.
Can you add a case for methods with the same name that shouldn't be accessible? Eg:
class Program
{
static void Main(string[] args)
{
}
private void Foo() { }
}
class G : Program
{
protected void Foo() { }
}| } | ||
|
|
||
| [Fact] | ||
| public async Task SignatureHelpForOverloadedInaccesibleMethods() |
There was a problem hiding this comment.
Can you also add a test to verify behavior if there's an extension method with the same name?
| } | ||
|
|
||
| [Fact] | ||
| public async Task SignatureHelpForInheritedInaccesibleMethods() |
There was a problem hiding this comment.
I'd like to see a few more tests for variants of this scenario:
-
An inherited protected method appears in signature help within the a sub type.
class A { protected void M1() { } } class B : A { void M2() { $$ // Both M1() and M2() } }
-
An inherited protected method appears in signature help after
this..class A { protected void M1() { } } class B : A { void M2() { this.$$ // Both M1() and M2() } }
-
An inherited protected method appears in signature help after
base..class A { protected void M1() { } } class B : A { void M2() { base.$$ // Only M1() } }
-
Only static methods appear in a static context.
class A { protected static void M1() { } public void M2() { } } class B : A { static void M3() { A.$$ // Only M1() } public void M4() { } }
class A { protected static void M1() { } public void M2() { } } class B : A { static void M3() { B.$$ // Both M1() and M3() } public void M4() { } }
-
Static and instance methods appear in an instance context.
class A { protected static void M1() { } public void M2() { } } class B : A { static void M3() { $$ // M1(), M2(), M3() and M4() appear } public void M4() { } }
cbfe81a to
132b636
Compare
132b636 to
9cb58b4
Compare
|
|
||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | ||
| <LangVersion>7.1</LangVersion> | ||
| </PropertyGroup> |
There was a problem hiding this comment.
Instead of adding this to a particular project, could you remove this properties here and just add <LangVersion>7.1</LangVersion> to https://github.com/OmniSharp/omnisharp-roslyn/blob/master/build/Settings.props so that all projects are using C# 7.1?
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; |
There was a problem hiding this comment.
Sort usings. System namespaces go first. (Note that this is a setting in Visual Studio)
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| namespace OmniSharp.Roslyn.CSharp.Services.Signatures |
There was a problem hiding this comment.
Missing a line between the using directives and the namespace.
| using System.Linq; | ||
| namespace OmniSharp.Roslyn.CSharp.Services.Signatures | ||
| { | ||
| static internal class CheckForStatic |
There was a problem hiding this comment.
Sort modifiers. "internal" before "static"
There was a problem hiding this comment.
By convention, classes containing extension methods are typically named with an Extensions suffix?
| // any other location is considered static | ||
| return true; | ||
| } | ||
| public static SyntaxTokenList GetModifiers(SyntaxNode member) |
There was a problem hiding this comment.
Missing a blank line between methods.
| { | ||
| var throughExpression = ((MemberAccessExpressionSyntax)invocation.Receiver).Expression; | ||
| throughSymbol = invocation.SemanticModel.GetSpeculativeSymbolInfo(invocation.Position, throughExpression, SpeculativeBindingOption.BindAsExpression).Symbol; | ||
| throughType = invocation.SemanticModel.GetSpeculativeTypeInfo(invocation.Position, throughExpression, SpeculativeBindingOption.BindAsTypeOrNamespace).Type; |
There was a problem hiding this comment.
Why do we need to retrieve speculative symbol and type info here?
There was a problem hiding this comment.
Our heuristic is to show instance members if the thing left of the dot can bind as a non-type and to show static members if the thing left of the dot can bind as a type (and both, in the case of Color Color).
There was a problem hiding this comment.
Is this just copied and pasted from Roslyn?
There was a problem hiding this comment.
Roslyn uses a different API for doing the same thing.
There was a problem hiding this comment.
Roslyn does awkward type lookup to achieve the same effect: http://source.roslyn.io/#Microsoft.CodeAnalysis.CSharp.Features/SignatureHelp/InvocationExpressionSignatureHelpProvider_MethodGroup.cs,44. @DustinCampbell does what they are doing seem materially different to you?
There was a problem hiding this comment.
Yup. It's similar. This looks fine.
| throughSymbol = invocation.SemanticModel.GetSpeculativeSymbolInfo(invocation.Position, throughExpression, SpeculativeBindingOption.BindAsExpression).Symbol; | ||
| throughType = invocation.SemanticModel.GetSpeculativeTypeInfo(invocation.Position, throughExpression, SpeculativeBindingOption.BindAsTypeOrNamespace).Type; | ||
| var includeInstance = throughSymbol != null && !(throughSymbol is ITypeSymbol); | ||
| var includeStatic = (throughSymbol is INamedTypeSymbol) || throughType != null; |
There was a problem hiding this comment.
Why are the two clauses in the statements above reversed? Was that intentional? Why are we checking for null? Doesn't the is check already do that for you?
There was a problem hiding this comment.
In the first case if we remove the null check,then if throughSymbol is null,the second statement will evaluate to true (and we want it to be false on a null).
In the second case one statement is checking throughSymbol and the other throughType, so we need to have both of them.
There was a problem hiding this comment.
I see it now -- thanks!
| methodGroup = methodGroup.Where(m => (m.IsStatic && includeStatic) || (!m.IsStatic && includeInstance)); | ||
| } | ||
|
|
||
| else if (invocation.Receiver is SimpleNameSyntax && invocation.IsStatic) |
There was a problem hiding this comment.
Unnecessary blank line above this.
|
|
||
| }"; | ||
| var actual = await GetSignatureHelp(source); | ||
| Assert.Single(actual.Signatures); |
There was a problem hiding this comment.
Should we verify which signature we got?
There was a problem hiding this comment.
Yes we should.Added that.
| var actual = await GetSignatureHelp(source); | ||
| Assert.Equal(4, actual.Signatures.Count()); | ||
| } | ||
| [Fact] |
There was a problem hiding this comment.
Missing a blank line before this method
| public SyntaxNode Receiver { get; } | ||
| public IEnumerable<TypeInfo> ArgumentTypes { get; } | ||
| public IEnumerable<SyntaxToken> Separators { get; } | ||
| public bool IsStatic { get; } |
| { | ||
| var throughExpression = ((MemberAccessExpressionSyntax)invocation.Receiver).Expression; | ||
| throughSymbol = invocation.SemanticModel.GetSpeculativeSymbolInfo(invocation.Position, throughExpression, SpeculativeBindingOption.BindAsExpression).Symbol; | ||
| throughType = invocation.SemanticModel.GetSpeculativeTypeInfo(invocation.Position, throughExpression, SpeculativeBindingOption.BindAsTypeOrNamespace).Type; |
There was a problem hiding this comment.
Our heuristic is to show instance members if the thing left of the dot can bind as a non-type and to show static members if the thing left of the dot can bind as a type (and both, in the case of Color Color).
| { | ||
| var semanticModel = await document.GetSemanticModelAsync(); | ||
| return new InvocationContext(semanticModel, position, invocation.Expression, invocation.ArgumentList); | ||
| return new InvocationContext(semanticModel, position, invocation.Expression, invocation.ArgumentList, invocation.IsInStaticContext()); |
There was a problem hiding this comment.
Can InvocationContext take care of calling IsInStaticContext()? You've making the same call in all the initializations.
There was a problem hiding this comment.
In that case we will have to pass the invocation object to InvocationContext and since there are three different types of such objects we will have to add another constructor in InvocationContext. Which is better of the two ?
| { | ||
| var semanticModel = await document.GetSemanticModelAsync(); | ||
| return new InvocationContext(semanticModel, position, objectCreation, objectCreation.ArgumentList); | ||
| return new InvocationContext(semanticModel, position, objectCreation, objectCreation.ArgumentList,objectCreation.IsInStaticContext()); |
There was a problem hiding this comment.
Missing comma between objectCreation.ArgumentList and objectCreation
| @@ -1,4 +1,6 @@ | |||
| using System; | |||
| // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | |||
There was a problem hiding this comment.
You might add a comment for where this was copied from.
|
|
||
| namespace OmniSharp.Roslyn.CSharp.Services.Signatures | ||
| { | ||
| //TO DO: Reomove this class once a public API for Signature Help from Roslyn is available |
| { | ||
| var throughExpression = ((MemberAccessExpressionSyntax)invocation.Receiver).Expression; | ||
| throughSymbol = invocation.SemanticModel.GetSpeculativeSymbolInfo(invocation.Position, throughExpression, SpeculativeBindingOption.BindAsExpression).Symbol; | ||
| throughType = invocation.SemanticModel.GetSpeculativeTypeInfo(invocation.Position, throughExpression, SpeculativeBindingOption.BindAsTypeOrNamespace).Type; |
There was a problem hiding this comment.
Yup. It's similar. This looks fine.
Issue : dotnet/vscode-csharp#1440
During the search for the signature help, only the immediate Type that contains the given method was being searched for possible method overloads hence the overloads present in the Base Classes were not appearing. Added the code to search for members using the GetMemberGroup. Added a test for the same.
Please review @DustinCampbell @TheRealPiotrP @rchande