Teach pattern-matching analyzer to detect and fix additional cases#19907
Teach pattern-matching analyzer to detect and fix additional cases#19907CyrusNajmabadi merged 1 commit intodotnet:masterfrom
Conversation
579bee5 to
a067452
Compare
|
Thanks for the contribution - tagging @dotnet/roslyn-ide for review. |
There was a problem hiding this comment.
Should be TryFind since this can fail. Maybe say "TryFind..InContainingBlock" as well. That would help explain why we need to hand write this instead of just using the ILocalSymbol and getting the declaration from that.
There was a problem hiding this comment.
How can this happen?
There was a problem hiding this comment.
It can't, just to satisfy the compiler. is there an ExceptionUtilities.Unreachable accessible to use?
There was a problem hiding this comment.
Yes. It's called "ExceptionUtilities.Unreachable" :D
There was a problem hiding this comment.
I think that's not accessible in feature project. If not could I add it?
There was a problem hiding this comment.
It should be accessible. We link that file into the Workspaces layer IIRC. Let me check.
There was a problem hiding this comment.
i woudl personally prefer ifOrWhileStatement
There was a problem hiding this comment.
it might be nice to keep this around, so other calls you make could access this directly. your call.
There was a problem hiding this comment.
shoudl be declarationStatement. "declaration" makes me think it has type VariableDeclaration.
There was a problem hiding this comment.
We'll need to figure out if we need a new option here. Or if we need to update the option we show to the user here. I think it's ok to have a single option, but we likely need to call out these cases in the preview. What do you think @Pilchie ?
There was a problem hiding this comment.
I personally don't think we need a new option here, since this isn't available on the command line yet.
@kuhlenh how do you feel about the preview question?
There was a problem hiding this comment.
It's unclear to me why so much logic changed in this method. Can you explain what your new code does that's different from teh old way of checking def assignment? Thanks!
There was a problem hiding this comment.
two reasons: in the old code if assigned was false we didn't run the loop body, so we could break out of loop early. and the other reason is that, unlike if, pattern var is scoped only inside while block.
There was a problem hiding this comment.
declarationStatement.
|
This is awesome. Mostly nits. Haven't reviewed the def assignment stuff yet. Waiting on you for explanation first. |
|
Other cases I want this to handle are:
var x = e as T;
if (condition && x != null && x.Foo)
// gives
if (condition && e is T x && x.Foo)
var x = e as T;
if (x == null) return;
// gives
if (!(e is T x)) return;
var x = e as T;
return x != null ? x.Foo : ...;
// gives
return e is T x ? x.Foo : ...;These all should be able to mix e.g. ternary+negative+inline, while+positive+non-leftmost etc. Before I do anything about it (probably in another PR?) I'd like to hear your thoughts on those. Thanks. |
|
Can you give examples of each of those? |
|
The more i think about this, the more i'm ok with all of this. I think the general feature we're offering is "use the new 'is Type T' pattern matching construct". So wherever reasonable we can see something that can translate into that pattern, we should offer it. |
|
(and yes, another PR would be good) |
|
Updated my previous comment to include some examples. |
Will this work with definite assignment? |
This is iffy. Because some people don't like the Note: i would personally like this. |
If it's just "return ternary" then yes please. |
|
Seperate PR for those though. ideally one PR per new addition. |
There was a problem hiding this comment.
Nit: Generally would prefer all comments to be "Sentence case."
There was a problem hiding this comment.
Tagging @mavasani . It seems unfortunate that we will get tons of callbacks for a file and we'll have to do this check and bail out for every node we get called back on. It would be really nice if we could have some sort of conditional way to say "don't even call into us for this syntax tree."
There was a problem hiding this comment.
@mavasani RegisterSyntaxNodeAction could take a SyntaxTree predicate that we could potentially use to avoid calling this action if the SyntaxTree didn't match.
There was a problem hiding this comment.
fyi, i'm a bit on the fence about this. In general we'd like to do some quick up front checks that don't involve expensive typechecks. That way we can be as low overhead as possible since we're going to be called on every if-statement in the world. so doing IsParentKind(Block) (and doing all the other syntactic checks first) before doing type-tests is generally preferred.
There was a problem hiding this comment.
I agree with you but I think it's not just this instance, for example OfType on all nodes to find identifiers or local declarations should be updated as well.
I propose we add bool IsKind<T>(this SyntaxNode, SyntaxKind, out T) where T : SyntaxNode.
There was a problem hiding this comment.
I wish there was a way to get the corresponding SyntaxKind from the syntax node type
There was a problem hiding this comment.
assignment.
try to wrap at 100col for comments.
There was a problem hiding this comment.
might want to comment/give-example why this is ok.
There was a problem hiding this comment.
This is exactly what the old code does (though not so obviously).
There was a problem hiding this comment.
Nevertheless, I think CheckDefnitiveAssignment is oversimplified. what if the statement is branched, etc? Could we use control flow analysis instead?
There was a problem hiding this comment.
it's not clear to me why this is appropriate. do you have an example?
There was a problem hiding this comment.
It's not. when we return true here we won't suggest the fix.
|
@CyrusNajmabadi I've addressed your comments, Had a few questions. Can you please take a look? thanks. |
CyrusNajmabadi
left a comment
There was a problem hiding this comment.
I like the new extension. If you're feeling up to it, feel free to update the rest of our code to use it :)
|
We could even have overloads for node types then you dont need to pass the syntax kind, static bool Is(this SyntaxNode node, out LocalVarDeclStatement result)
// etc
if(node.Is(out LocalVarDeclStatement decl))We could have it for abstract classes as well. Maybe the syntax generator could produce these helpers. Is that too many overloads? |
|
Can't tell if serious. How would that overload work? |
|
The generated code would look like this: static bool Is(this SyntaxNode node, out LocalDeclarationStatementSyntax result)
=> node.IsKind(SyntaxKind.LocalDeclarationStatement, out result);
static bool Is(this SyntaxNode node, out EnumDeclarationSyntax result)
=> node.IsKind(SyntaxKind.EnumDeclaration, out result);I'm not sure if we should do this for abstract classes? e.g. static bool Is(this SyntaxNode node, out TypeDeclarationSyntax result) {
=> node.IsKind(ClassDeclaration, InterfaceDeclaration, StructDeclaration, out result); |
|
Ah, i see. I missed the part where this would be autogenerated. I would be fine with you making such a helper. But not right now :) |
|
Ok I got it here in this gist, will open a PR at a later time. |
|
@CyrusNajmabadi Anything more you are waiting on here? If not, any reason not to merge this? |
|
I've signed off :) |
|
retest this please |
|
Test results were fairly stale. |
- null check in `while` in addition to `if` - inline type check e.g. `if ((o as T) != null)`
|
Thanks! |

Fixes #17744
whilein addition toifif ((x = o as T) != null)/cc @CyrusNajmabadi