How to transform to SSA form and other compiler-isms in AST

I'm writing a C to Rust converter. So far it's mostly AST to AST conversion, but I'd like to add smarter transformations, and it's starting to look like a proper compiler.

For example, I'd like to translate:

int i;
for(i = 0; i < 10; i++) {}
for(i = 0; i < 10; i++) {}

to

for i in 0..10 {}
for i in 0..10 {}

but this is only valid if I recognize that i is not used outside the loop (with taking scoping rules into account), and to do that I need to recognize that the two assignments to i make it de-facto two separate variables. Basically, I need to identify which variables can be changed into SSA form (without help of φ), and then track their usage.

However, I'm currently only using AST as the only representation of the code and only visitor pattern to work with the code, but that doesn't seem to be suitable for the task. Compiler literature suggests working with basic blocks/CFG, but I don't think I can "desugar" the AST without losing the high-level form of code that I want to preserve.

I presume rustc had to deal with such things before MIR, so how did you do it?