Conditional compilation does not parse implicit return correctly

I would have expected this code to work (playground link):

fn foo() -> &'static str {
    #[cfg(feature = "foo")]
    "foo"
    
    #[cfg(not(feature = "foo"))]
    "bar"
}

When foo is enabled, the "bar" expression doesn't exist at all, so "foo" is the last expression in the function, which should be valid. But it's currently not parsed that way; without an explicit return, you get "error: expected ;, found #".

Is this a bug, or is it intentional?

That's two consecutive expressions, which is not allowed syntactically. Conditional compilation doesn't make source text go away.

If you think about it: in order to interpret the cfg attributes, the compiler must already have parsed the code. Since the attribute may very well have been something else (eg. a lint, an attribute macro, etc.), the compiler can't possibly just remove the cfg'd-out tokens before parsing. Disabling code has to happen after parsing, which means that the whole thing, with both "branches" and attributes included, has to be syntactically valid.

3 Likes

As a rule of thumb is better to apply conditional compilation attributes on fns/methods rather than their bodies despite the downsides of added maintenance and seemingly duplicate code.

There's also the cfg-if crate. This lets you write the above in a manner that makes the possible control flow a little easier to read:

fn foo() -> &'static str {
    cfg_if! {
        if #[cfg(feature = "foo")] {
            "foo"
        } else {
            "bar"
        }
    }
}
2 Likes

Ah. Yep, I definitely imagined that conditional config would actually prevent nodes from being included in the AST, but you're right, that doesn't really make sense.

That is definitely possible. That's not the problem.

The problem is that you have to have an AST for that in the first place, for which syntactically correct tokens are required.

...right, by "prevent nodes from being included in the AST," I meant it would prevent inclusion in any AST. It was just a case of...let's call it lingering C-preprocessor mental maladaptation.

That works really well; thanks for sharing!