In Which Code The Parsing Of Function Happen In Rustc?

Hello, I'm trying to experiment Rust with semicolon inference. For now, it can handle this, except the handling of call function that has return value at the last code of function that does not has return type isn't inserted semicolon yet

#![no_std]

fn return_i32() -> i32 {
    let a = 10
    let b = 20
    a + b
}

fn no_return() {
    let a = 
    return_i32
()
    let b = 30
}

fn expect_i32() -> i32 {
    let a = if 2 == 2 {
        let b = return_i32()
        b * 4
    } else {
        2 * 4
    }
    return_i32()
}

fn expect_no_return() {
    return_i32()
}

The result :

#![feature(prelude_import)]
#![no_std]

#![no_std]
extern crate core;
#[prelude_import]
use ::core::prelude::rust_2015::*;

fn return_i32() -> i32 { let a = 10; let b = 20; a + b }

fn no_return() { let a = return_i32(); let b = 30; }

fn expect_i32() -> i32 {
    let a = if 2 == 2 { let b = return_i32(); b * 4 } else { 2 * 4 };
    return_i32()
}

// this should be added semicolon, I'm trying to implement it
fn expect_no_return() { return_i32() }

I need to get boolean info whether current function has return type or not, can anyone pinpoint in which code the parsing of function happens? With direct link to the github file

The problem you're trying to solve is unsolvable because sometimes, both () and a non-() type are valid return types from the function. For example (playground):

trait Semicoloned {
    const SEMICOLONED: bool;
    
    fn detect_semicolon(&self) {
        if Self::SEMICOLONED {
            println!("Semicolon!");
        } else {
            println!("No semicolon!");
        }
    }
}
impl Semicoloned for () {
    const SEMICOLONED: bool = true;
}
impl Semicoloned for i32 {
    const SEMICOLONED: bool = false;
}

fn i32_with_side_effects() -> i32 {
    println!("Hi!");
    5
}

fn demo1() -> impl Semicoloned {
    i32_with_side_effects()
}
fn demo2() -> impl Semicoloned {
    i32_with_side_effects();
}

fn main() {
    demo1().detect_semicolon();
    demo2().detect_semicolon();
}

(The same problem can happen with blocks as well as functions; think about the difference between { i32_with_side_effects() } and { i32_with_side_effects(); }.)

Even outside cases like this, you are trying to feed back information from type checking into the parser, which doesn't really make sense because the parser runs so much earlier. Leaving the -> T off a function/method causes it to return (), but there are other ways to return () (e.g. writing -> () explicitly, or writing -> Self::AssociatedType where the associated type happens to be ()).

Automatic semicolon insertion would therefore probably need to be done in the type checker, if you did it at all (i.e. replace an optional ; with a coercion site that sometimes coerces anything into (). However, as my example above shows, sometimes both possibilities are reasonable.

There's also the chance that semicolon insertion entirely changes the meaning of the program, e.g. if m is a Mutex<()>, then let guard = { m.lock() }; will lock the mutex for the rest of the scope of guard, whereas let guard = { m.lock(); }; will lock the mutex for the duration of the statement m.lock(). The former is probably what you meant, but the latter is still both syntactically valid and meaningful.

Thank you for the feedback. In my current result, after I implement the one that I'm asking. It will become like this

fn i32_with_side_effects() -> i32 {
    println!("Hi!") // will have semicolon
    5 // no semicolon
}

fn demo1() -> impl Semicoloned {
    i32_with_side_effects() // no semicolom
}
fn demo2() -> impl Semicoloned {
    i32_with_side_effects() // no semicolon
}

// if wants to return (), the code is

fn demo2() -> impl Semicoloned {
    i32_with_side_effects() // semicolon
    () // no semicolon
}

Is there real valid end bug example, I mean by valid is the one that causes damage, because of that?

What I understand now is it can cause program that previously compile becomes must be fixed, kind like breaking change. But what is the valid end bug example, for example let's say it causes dangling reference, I want to know the real effect like that

What I'm trying to do is more like embedding proc macro inside the parser. So even knowing symbol -> is enough, it already tells the user write return value then no semicolon is inserted

What is the meaningfull effect that this causes?

let guard = { m.lock(); }; 

Because in my view, that is just lock and instantly unlock again. What is the real valid effect example?

Yeah my current result if writing

let guard = { m.lock() }

Become

let guard = { m.lock() };

My goal is also if there is already semicolon, no semicolon will be insterted. That means I can write semicolon for specific part, assuming doing it gives valid effect

I also has another idea by adding new keyword finish as replacement to the return without keyword. So finish will exit frok block, where return will exit from function. With that way I feel like now I can implement the semicolon inference without having that difference you mentioned, is it true?. But with downside can't compile old code that uses return value without keyword return. But that is ok because only my usage

If I move to implement this in type check section, what is the high level process that I should do? Because currently in the parser section, I do it by reading until it becomes valid expression, then adding semicolon