Fixing 'else' keyword completion#25703
Conversation
| Console.WriteLine(); | ||
| } | ||
| $@"if (true) | ||
| {statement} |
There was a problem hiding this comment.
i personally dislike this. I cannot see what's happening. My tests are now non-local.
The only sort of 'theory' approach i've ever been ok with is targetted, simple application of theories, where the theory is on hte test method itself. if i have to look up more than 2-3 lines, or i have to go look at another class/method etc. then i've completely lost the plot on what the test is testing. Previously i could see this exactly. Now i can't. this is a net negative for me here.
There was a problem hiding this comment.
ok I can revert it back... it just seemed to me like this way it's much easier to read, review and spot bugs in the tests, whereas with a lot of duplication it's really easy to miss something
There was a problem hiding this comment.
and also more importantly, when writing more tests people will usually avoid writing all the cases and just do some of them (or more likely just one) to not bother with writing so much duplication, which can sometimes miss bugs due to missing test cases
There was a problem hiding this comment.
IMO, tests for this sort of thing should be written based on the expected interesting cases based on the code change. So, your change adds an arbitrary walk upwards looking for if-statements. And it also checks that if-statement for the last token of the true-statement within it.
It seems like we could test this out with some very judicious tests that would demonstrate that both of those pieces of logic are working properly. For example, we sholud not see 'else' here:
if (true)
Console.WriteLine()
$$Because the last token of hte true-statement would be the missing-token ; and htat's not what we're on. Similarly, we should see if after Console.WriteLine();. And we should see if htis if it deeply nested. Testing one level deep, two levels deep, and something larger should be sufficient.
You also removed some existing branches in the original code. We shoudl ensure appropriate behavior remains for those.
This does not seem like a lot of tests needed. It tests positive and negative cases. And feels appropriate given the code change.
There was a problem hiding this comment.
Thanks, I'll add tests for that too.
There was a problem hiding this comment.
it just seemed to me like this way it's much easier to read, review and spot bugs in the tests
It's harder for me, because i ca'nt just look at a test and know what it means. I have to go analyze other parts of the test file to know. We try to avoid that. I've accepted some Theories in hte past where the theory travels with the test method. and the theory itself is very tiny. i.e. i've seen theories that try to do major replacements in the code, and def make it less clear.
I'm on the fence here about this one. But, give my previous post, i don't think it adds substantial value given just writing the 6ish test cases manually.
There was a problem hiding this comment.
Yes. this form seems ok to me. i can live with inline data at individual test sites.
CyrusNajmabadi
left a comment
There was a problem hiding this comment.
I like the code change. i don't like the test change. that said, i'm sympathetic about the amount of duplication you feel you're incur here.
|
|
||
| [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] | ||
| public async Task TestAfterIfBlock() | ||
| class Statements : DataAttribute |
To me this i a non-problem. I don't think of test quantity as being "overwhelming". It's just a lot of tests. There are still the same number of tests when you use theories, you just have used a generation technique to produce the tests, which i think decreases clarity. At the end of hte day you generally have 100% test coverage until you break something and either a single test breaks, or a set of tests break. You go and look at those tests, figure it out and rinse/repeat. The number of total tests isn't really relevant here. |
|
@CyrusNajmabadi What if I put InlineData on each test instead but still kept it as a theory? How would you feel about that? |
| if (token.IsKind(SyntaxKind.CloseBraceToken) && ifStatement.Statement is BlockSyntax && token == ((BlockSyntax)ifStatement.Statement).CloseBraceToken) | ||
| { | ||
| return true; | ||
| if (ifStatement.Statement.GetLastToken(includeSkipped: true, includeZeroWidth: true) == token) |
There was a problem hiding this comment.
I actually have no idea why includeSkipped: true is needed here. There's no test that covers this, everything passes either way.
There was a problem hiding this comment.
IncludeSkipped is likely appropriate, though harder to test for. It basically means, "if the parser ran into stuff it totally didn't understand, but it attached it to that staement, then treat hte last token of that skipped run of tokens as the last token of the statement.
There was a problem hiding this comment.
It seems really unlikely that there would be a skipped semicolon at the end. That usually parsers as a separate empty statement.
There was a problem hiding this comment.
In the original implementation, else is not offered in code like this (because the last token including skipped is not a semicolon or a close brace):
if (true)
Console.WriteLine();,But with this PR, it is offered. Which is the expected behavior here? To offer or not offer? I will add a test either way. If it's to not offer, includeSkipped should actually be removed.
There was a problem hiding this comment.
Which is the expected behavior here?
The expected behavior is whatever is easiest to provide :)
You could literally have this work, and that would be ok. Or it could not work, and that would be ok.
There was a problem hiding this comment.
whatever is easiest to provide
The difference is just in setting includeSkipped to either true or false. Then I guess easiest to provide would be to not specify the argument?
There was a problem hiding this comment.
Given that there is no completion for anything after such skipped tokens and the fact that if you type else at that point it doesn't actually end up being an else clause (it becomes a skipped token too), I'm removing this.
I would probably be ok with that. though i like the InlineData to be small to be clear as to what's going on. In your case, it's likely ok. But it's not a blanket rule that that's ok for all tests. |
| if (token.IsKind(SyntaxKind.CloseBraceToken) && ifStatement.Statement is BlockSyntax && token == ((BlockSyntax)ifStatement.Statement).CloseBraceToken) | ||
| { | ||
| return true; | ||
| if (ifStatement.Statement.GetLastToken(includeZeroWidth: true) == token) |
There was a problem hiding this comment.
warrants a comment. but not a big deal.
|
retest windows_debug_vs-integration_prtest please |
|
retest windows_debug_vs-integration_prtest please |
|
retest windows_debug_spanish_unit32_prtest please |
|
@dotnet/roslyn-ide Can this community PR get a final review? |
fixes #25336
I tried rewriting the tests using theory at the end to make them less overwhelming and significantly reduce the repetition, but I'm not sure if it's the right thing to do considering that this sort of approach isn't used in any other tests.
@CyrusNajmabadi I think I remember you saying that tests should contain the literal code so that it's easy to understand each one separately and we should generally avoid using any sort of helpers. Do you think what I did here is acceptable?