Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Omnisharp VS Code Issue 1814 Fix #1007

Merged
merged 15 commits into from
Nov 9, 2017

Conversation

akshita31
Copy link
Contributor

dotnet/vscode-csharp#1814

The issue was arising because there was no code to handle the AttributeSyntax type node(which is the case for the Attribute Constructors). But there was a difference between the type of arguments being used by the InvocationExpressionSyntax class and the AttributeSyntax class.Hence instead of keeping the entire Arguments object, we pulled out the relevant properties at the initial stage itself, and a change was made to the InvocationContext class accordingly. A test case has also been added to verify the behavior.
Please review @rchande @TheRealPiotrP @DustinCampbell .

Copy link
Member

@david-driscoll david-driscoll left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor changes from me, will let @DustinCampbell review too.

@@ -0,0 +1,3 @@
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should probably drop this file. 😄

@@ -96,27 +97,43 @@ public SignatureHelpService(OmniSharpWorkspace workspace)
var invocation = node as InvocationExpressionSyntax;
if (invocation != null && invocation.ArgumentList.Span.Contains(position))
{
var i = await document.GetSemanticModelAsync();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably give i a slightly better name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call this semanticaModel and replace the call to await document.GetSemanticModelAsync() below with the variable as well. There's no need to call it twice.

@@ -153,6 +153,7 @@ public class MyClass
}
}


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra whitespace

Copy link
Contributor

@DustinCampbell DustinCampbell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work fixing the bug! There's just some code clean up left to do.

@@ -1,13 +1,21 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: System namespaces should be sorted on top.

@@ -96,27 +97,43 @@ public SignatureHelpService(OmniSharpWorkspace workspace)
var invocation = node as InvocationExpressionSyntax;
if (invocation != null && invocation.ArgumentList.Span.Contains(position))
{
var i = await document.GetSemanticModelAsync();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call this semanticaModel and replace the call to await document.GetSemanticModelAsync() below with the variable as well. There's no need to call it twice.

return new InvocationContext()
{
SemanticModel = await document.GetSemanticModelAsync(),
Position = position,
Receiver = invocation.Expression,
ArgumentList = invocation.ArgumentList
ArgumentTypes = invocation.ArgumentList.Arguments.Select(argument => i.GetTypeInfo(argument.Expression)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the expression here getting complex enough to extract into a local?

var objectCreation = node as ObjectCreationExpressionSyntax;
if (objectCreation != null && objectCreation.ArgumentList.Span.Contains(position))
{
var i = await document.GetSemanticModelAsync();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

semanticModel rather than i

return new InvocationContext()
{
SemanticModel = await document.GetSemanticModelAsync(),
Position = position,
Receiver = invocation.Expression,
ArgumentList = invocation.ArgumentList
ArgumentTypes = invocation.ArgumentList.Arguments.Select(argument => i.GetTypeInfo(argument.Expression)),
Separators = invocation.ArgumentList.Arguments.GetSeparators()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as above

Position = position,
Receiver = objectCreation,
ArgumentList = objectCreation.ArgumentList
ArgumentTypes = objectCreation.ArgumentList.Arguments.Select(argument => i.GetTypeInfo(argument.Expression)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akshita31 we can extract this logic into an extension method or a private function.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd consider adding two constructor overloads to InvocationContext which will deal with this initialization. If we want to keep InvocationContext a POCO [Plain Old C# Object] then we can put this logic into a factory method instead. I don't have a strong preference.

ArgumentTypes = attributeSyntax.ArgumentList.Arguments.Select(argument => i.GetTypeInfo(argument.Expression)),
Separators = attributeSyntax.ArgumentList.Arguments.GetSeparators()
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments as above.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you avoid copying and pasting the same code multiple times?

Position = position,
Receiver = objectCreation,
ArgumentList = objectCreation.ArgumentList
ArgumentTypes = objectCreation.ArgumentList.Arguments.Select(argument => i.GetTypeInfo(argument.Expression)),
Separators = objectCreation.ArgumentList.Arguments.GetSeparators()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here

Assert.Single(signature.Parameters);
Assert.Equal("value", signature.Parameters.ElementAt(0).Name);
Assert.Equal("int value", signature.Parameters.ElementAt(0).Label);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary blank line


namespace OmniSharp.Roslyn.CSharp.Services.Signatures
{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lots of extra whitespace here.

@@ -115,7 +115,7 @@ public SignatureHelpFacts(ITestOutputHelper output)
public static void Main(){
Foo($$);
}
pubic static Foo(bool b, int n = 1234)
public static Foo(bool b, int n = 1234)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have we verified that this code was not intentionally invalid previously?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, didn't verify that. Should I revert it back to the previous state ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't think so. This doesn't look intentional to me based on the test name.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should look at the test and decide whether the typo was intentional :)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My guess, based on what's being tested, is that it was unintentional.

int value;
public MyTestAttribute(int value)
{
this.value =value;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add a space between = and value

void TestMethod(int value){
this.value = value;
}
public class MyTestAttribute : Attribute {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

newline here

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C# code typically puts opening { on a new line. Consider using VS to format the document as this will get your style in-line with C#.

@@ -153,6 +153,42 @@ private int Foo(int one, int two, int three)
}

[Fact]
public async Task AttributeConstructorSignatureHelp()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rchande the tests for signatures in Constructor/Method invocations cover a bunch of scenarios. Is this single test sufficient to cover attribute signatures?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akshita31 the test name here seems misaligned with the names of other tests in this file. Since we're replicating a test behavior in a new syntax node, can you align the test names between this and constructor/method signatures?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was confused by the "1st position" comment, but looks like there is plenty of other coverage.

var types = invocation.ArgumentList.Arguments
.Select(argument => invocation.SemanticModel.GetTypeInfo(argument.Expression));
var types = invocation.ArgumentTypes;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary empty lines

Copy link

@TheRealPiotrP TheRealPiotrP left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @akshita31! The change looks good overall. Like others, I have some style feedback to keep the code in-line with the rest of the repo. I'd also like to be sure that you and @rchande are comfortable with the level of test coverage here. Thanks for the contribution!

Copy link

@rchande rchande left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good--minor nits.


namespace OmniSharp.Roslyn.CSharp.Services.Signatures
{

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blank lines

}


Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blank lines

}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blank line

@@ -96,27 +97,43 @@ public SignatureHelpService(OmniSharpWorkspace workspace)
var invocation = node as InvocationExpressionSyntax;
if (invocation != null && invocation.ArgumentList.Span.Contains(position))
{
var i = await document.GetSemanticModelAsync();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused?

return new InvocationContext()
{
SemanticModel = await document.GetSemanticModelAsync(),
SemanticModel = i,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see--can you make this consistent with above, either with "i" or just calling GetSemanticModel?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also "i" is probably not the best name :)

@@ -153,6 +153,7 @@ public class MyClass
}
}


Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line

@@ -115,7 +115,7 @@ public SignatureHelpFacts(ITestOutputHelper output)
public static void Main(){
Foo($$);
}
pubic static Foo(bool b, int n = 1234)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol

@@ -0,0 +1,3 @@
{
"lockfileVersion": 1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intenentional?

const string source =
@"using System;

namespace New_folder
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you align the quoted string to the left margin? Makes it easier to tell what the test contents are

@@ -153,6 +153,42 @@ private int Foo(int one, int two, int three)
}

[Fact]
public async Task AttributeConstructorSignatureHelp()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the second parameter, or if there aren't any?

@DustinCampbell
Copy link
Contributor

@akshita31 : Your last commit didn't compile (you didn't replace references to i with semanticModel).

@@ -192,7 +192,7 @@ public MyTestAttribute(int value)
Assert.Equal("int value", signature.Parameters.ElementAt(0).Label);
}
[Fact]
public async Task SignatureHelpforAttCtorMultipleArg()
public async Task SignatureHelpforAttCtorMultipleParam()
{
const string source =
@"using System;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this pass? It doesn't look like there's a "$$" anywhere in the markup?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, I see it. Anyway, it seems like the test is called "MultipleParam", but you're only testing in the first parameter position.

double value2;
public MyTestAttribute()
{
this.value1 = 1;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can delete all the "value1" and "value2" related stuff since this test doesn't use them.

@@ -153,7 +153,7 @@ private int Foo(int one, int two, int three)
}

[Fact]
public async Task SignatureHelpforAttCtorSingleArg()
public async Task SignatureHelpforAttCtorSingleParam()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer that we not abbreviate "attribute".

}
node = node.Parent;
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't remove this line (the rule is to leave a blank line between a close brace and a statement).

public IEnumerable<SyntaxToken> Separators { get; set; }
public IEnumerable<SyntaxToken> Separators { get; set; }

public InvocationContext(SemanticModel SemModel,int Pos,SyntaxNode Rec, ArgumentListSyntax ArgList)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lowerCase parameter names.
space after comma
what does "rec" mean?

@@ -11,7 +12,24 @@ internal class InvocationContext
public int Position { get; set; }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not your fault, but do these properties actually need to be mutable? Can we have them be get-only?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes , it works with get-only.

}
public class MyTestAttribute : Attribute
{
int value1;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Up to you, but you can remove the fields and their assignment, if you want to make the test code even simpler.

public SemanticModel SemanticModel { get; }
public int Position { get; }
public SyntaxNode Receiver { get; }
public IEnumerable<TypeInfo> ArgumentTypes { get; }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra space

public class MyTestAttribute : Attribute
{
int value1;
double value2;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove these.

public class MyTestAttribute : Attribute
{
int value1;
double value2;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can still be removed?

@DustinCampbell
Copy link
Contributor

This is looking much better! Just a couple more tiny nits from me and that package-lock.json file looks like it was included unintentionally.

return new InvocationContext(semanticModel, position, objectCreation,objectCreation.ArgumentList);
}
var attributeSyntax = node as AttributeSyntax;
if (attributeSyntax != null && attributeSyntax.ArgumentList.Span.Contains(position))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can make this a little cleaner by using a pattern match expression:
if (node is AttributeSyntax attributeSyntax && attributeSyntax ... )

@@ -74,7 +72,6 @@ public SignatureHelpService(OmniSharpWorkspace workspace)
}
}
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leave this blank line here (the brace is followed by a statement).

@@ -90,24 +91,25 @@ public SignatureHelpService(OmniSharpWorkspace workspace)
// Walk up until we find a node that we're interested in.
while (node != null)
{
var invocation = node as InvocationExpressionSyntax;
if (invocation != null && invocation.ArgumentList.Span.Contains(position))

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, should have been more specific. Blank line between close brace and statement :).

Copy link
Contributor

@DustinCampbell DustinCampbell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple more nits and I'm OK with this change.

Assert.Equal("value", signature.Parameters.ElementAt(0).Name);
Assert.Equal("int value", signature.Parameters.ElementAt(0).Label);
}
[Fact]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing blank line before method. Should be between close brace and [Fact]

Assert.Equal("value2", signature.Parameters.ElementAt(1).Name);
Assert.Equal("double value2", signature.Parameters.ElementAt(1).Label);
}
[Fact]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing blank line before method. Should be between close brace and [Fact]

Copy link

@rchande rchande left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks!

ArgumentTypes = argList.Arguments.Select(argument => semModel.GetTypeInfo(argument.Expression));
Separators = argList.Arguments.GetSeparators();
}
public InvocationContext(SemanticModel semModel, int position, SyntaxNode receiver, AttributeArgumentListSyntax argList)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blank line after before this constructor please!

@DustinCampbell
Copy link
Contributor

@TheRealPiotrP : Have your requested changes been addressed?

@DustinCampbell DustinCampbell merged commit 9d26eb4 into OmniSharp:master Nov 9, 2017
@akshita31 akshita31 deleted the vscode_iss_1814 branch November 9, 2017 21:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants