Why does this "work"?

I recently resolved a bug that has been occupying my mind rent free for a while.

I tried to initialize a variable within a case statement. Apparently, this does not compile in standard C++ (I haven't tried it), but Arduino's build works perfectly fine without even a warning. Strangely, when I upload the sketch and run it, case 50 works fine, but cases 51 and 52 are completely ignored.

Why is that? And why doesn't Arduino's build command not raise it as an error?

                switch(incomingByte) {
                        case 48:
                                glow_white();
                                break;
                        case 49:
                                flash_red(); 
                                break;
                        case 50:
                                int ava = 0;   // this was the problem
                                do {
                                        rainbow();
                                        ava = Serial.available();
                                } (while ava == 0);
                                break;
                        case 51:
                                // Machine done.
                                // Shows red instead
                                glow_green();
                                break;
                        case 52:
                                // Machine off
                                led_off();
                                break;
                        default:
                                glow_white();
                }

It doesn't compile for me. I get an "error: expected 'while' before '(' token" from your malformed while statement. I also get the expected "crosses initialization of 'int ava'" error.

Please post your full sketch.

Note that you can initialize a variable in a case if you make it local to the case by wrapping the case code in braces.

variable declaration needs to be enclosed by a scope either globally or locally. A switch case label does not have a scope.
The following code is valid:

switch (1) {
   case 1: {
      int a=10;
   }
}

This compiles and works

void loop()
{
  static byte x = 0;
  switch (x)
  {
    case 0:  // not enclosed in braces
      byte y = 0;
      Serial.println(y);
      x++;
  }
}

Windows 10, IDE 1.8.10, Nano 33 IOT or a Nano

There has been a change in behaviour between versions of the IDE as I am used to seeing the "crosses initialization" error

This, however, does not compile

void loop()
{
  static byte x = 0;
  switch (x)
  {
    case 0:
      byte y = 0;
      Serial.println(y);
      x++;
      break;
    case 1:
      byte z = 0;
      Serial.println(z);
      x++;
      break;
  }
}

It produces a "jump to case label" error

UKHeliBob:
There has been a change in behaviour between versions of the IDE as I am used to seeing the "crosses initialization" error

I just tried compiling that code with the oldest version of the Arduino IDE I have installed, 1.0.5-r2 (which bundles avr-gcc 4.3.2) and it still compiles without errors.

The earliest version I have installed is much later than that and compiles the first code in reply #3 without error so the "crosses initialization" error must be caused by some other combination of circumstances

After fixing the obvious typo and adding enough of the missing code to attempt a compile:

void setup()
{
  Serial.begin(115200);
}


void glow_white(){};
void flash_red(){};
void rainbow(){};
void glow_green(){};
void led_off(){};


void loop()
{
  if (Serial.available())
  {
    byte incomingByte = Serial.read();


    switch (incomingByte) {
      case 48:
        glow_white();
        break;
      case 49:
        flash_red();
        break;
      case 50:
        int ava = 0;   // this was the problem
        do {
          rainbow();
          ava = Serial.available();
        } while (ava == 0);
        break;
      case 51:
        // Machine done.
        // Shows red instead
        glow_green();
        break;
      case 52:
        // Machine off
        led_off();
        break;
      default:
        glow_white();
    }
  }
}

I get the following warnings, which may explain why case 51 and case 52 don't work:

/Users/john/Documents/Arduino/sketch_dec25a/sketch_dec25a.ino: In function 'void loop()':
/Users/john/Documents/Arduino/sketch_dec25a/sketch_dec25a.ino:32:12: warning: jump to case label [-fpermissive]
       case 51:
            ^~
/Users/john/Documents/Arduino/sketch_dec25a/sketch_dec25a.ino:26:13: note:   crosses initialization of 'int ava'
         int ava = 0;   // this was the problem
             ^~~
/Users/john/Documents/Arduino/sketch_dec25a/sketch_dec25a.ino:37:12: warning: jump to case label [-fpermissive]
       case 52:
            ^~
/Users/john/Documents/Arduino/sketch_dec25a/sketch_dec25a.ino:26:13: note:   crosses initialization of 'int ava'
         int ava = 0;   // this was the problem
             ^~~
/Users/john/Documents/Arduino/sketch_dec25a/sketch_dec25a.ino:41:7: warning: jump to case label [-fpermissive]
       default:
       ^~~~~~~
/Users/john/Documents/Arduino/sketch_dec25a/sketch_dec25a.ino:26:13: note:   crosses initialization of 'int ava'
         int ava = 0;   // this was the problem
             ^~~
Sketch uses 1474 bytes (4%) of program storage space. Maximum is 32256 bytes.
Global variables use 184 bytes (8%) of dynamic memory, leaving 1864 bytes for local variables. Maximum is 2048 bytes.

This eliminates the warnings:

      case 50:
        {
          int ava = 0;   // this was the problem
          do {
            rainbow();
            ava = Serial.available();
          } while (ava == 0);
        }
        break;

As does this:

      case 50:
        do {
          rainbow();
        } while (Serial.available() == 0);
        break;

Sorry for the typos, I tried to just provide the minimal amount of code since my question was more philosophical than trying to solve a problem but I should have assumed that somebody would actually try compiling it.

While I have the Arduino IDE and packages installed (1.8.10), I use arduino-cli (0.7.0) on command-line Linux to build and uploaded sketches. It must be suppressing warnings, though I am not sure why the underlying gcc (avr-gcc-7.3.0) does not raise the compilation error.

For anyone that was curious, this is the full sketch. I have since fixed it, but changed it back to the failing state:

#include "Wire.h"
#include "Adafruit_NeoPixel.h"
 
#define PIN      6
#define N_LEDS  60

void glow_white();
void glow_green();
void flash_red();
void led_off();
void rainbow();
void rainbowCycle(int SpeedDelay);
byte * Wheel(byte WheelPos);
 
Adafruit_NeoPixel strip = Adafruit_NeoPixel(N_LEDS, PIN, NEO_RGBW + NEO_KHZ800);
static unsigned char incomingByte;

void setup() {
	Serial.begin(9600);
	strip.begin();
}

void loop() {
	if (Serial.available() > 0) {
		incomingByte = Serial.read();
		Serial.print("Arduino received: ");
		Serial.println(incomingByte);
		switch(incomingByte) {
			case 48:
				// Machine ready.
				glow_white();
				break;
			case 49:
				// Machine badness.
				flash_red();  // Why is this showing green?
				break;
			case 50:
				// Printing
				int ava = 0;
				while (ava == 0) {
					rainbow();
					ava = Serial.available();
				}
				break;
			case 51:
				// Machine done.
				// Shows red instead
				glow_green();
				break;
			case 52:
				// Machine off
				led_off();
				break;
			default:
				glow_white();
		}
	}
}


void glow_white() {
  for(uint16_t i=0; i<strip.numPixels()+4; i++) {
      strip.setPixelColor(i, strip.Color(0, 0, 0, 255));
  }
  strip.show();
}

// GRBW
void glow_green() {
  for(uint16_t i=0; i<strip.numPixels()+4; i++) {
      strip.setPixelColor(i, strip.Color(255, 0, 0, 0));
  }
  strip.show();
}

void flash_red() {
  
  for(uint16_t i=0; i<strip.numPixels()+4; i++) {
      strip.setPixelColor(i, strip.Color(0, 255, 0, 0));
  }
  strip.show();
}

void led_off() {
	strip.clear();
	strip.show();
}

void rainbow() {
	strip.clear();
  	rainbowCycle(2);
}

void rainbowCycle(int SpeedDelay) {
	byte *c;
	uint16_t i, j;

	uint16_t NUM_LEDS = strip.numPixels() + 4;
	for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
		for(i=0; i< NUM_LEDS; i++) {
			c=Wheel(((i * 256 / NUM_LEDS) + j) & 255);
			strip.setPixelColor(i, strip.Color(*c, *(c+1), *(c+2)));
		}
		strip.show();
		delay(SpeedDelay);
	}
}

byte * Wheel(byte WheelPos) {
	static byte c[3];
 
	if(WheelPos < 85) {
		c[0]=WheelPos * 3;
		c[1]=255 - WheelPos * 3;
		c[2]=0;
	} else if(WheelPos < 170) {
		WheelPos -= 85;
		c[0]=255 - WheelPos * 3;
		c[1]=0;
		c[2]=WheelPos * 3;
	} else {
		WheelPos -= 170;
		c[0]=0;
	c[1]=WheelPos * 3;
	c[2]=255 - WheelPos * 3;
	}
	return c;
}

You need to enclosed your variable declaration with initialization inside a {}.
The reason for this is since C/C++ allows cases to follow through, it is possible that you can skip a case where you declare your variable and use it in the next case. For example:

int test = 1;
switch (test) {
   case 0:
      int num = 3;
      Serial.println(num);
      break;
   case 1:
      Serial.println(num);
      break;
}

The above code is not allowed because "num" is still in-scope in case 1, but we don't know the value of num since we skipped its initialization in case 0.

That being said, the following 2 situations are valid:

switch (test) {
   case 0:
      int num;
      num = 3;
      Serial.println(num);
      break;
   case 1:
      num = 4; //if you remove this, code still compiles but value could be(?) garbage
      Serial.println(num);
      break;
}
switch (test) {
   case 0: {
      int num = 3;
      Serial.println(num);
      break;
   }
   case 1:
      break;
}

Bytemining:
Sorry for the typos, I tried to just provide the minimal amount of code since my question was more philosophical than trying to solve a problem but I should have assumed that somebody would actually try compiling it.

That's the first thing we do.

Bytemining:
why doesn't Arduino's build command not raise it as an error?

Because they use the -fpermissive compiler flag in Arduino AVR Boards' compilation recipes. This downgrades it from an error to a warning (but you'll only see the warning if you have them enabled).

UKHeliBob:
Windows 10, IDE 1.8.10, Nano 33 IOT or a Nano

There has been a change in behaviour between versions of the IDE as I am used to seeing the "crosses initialization" error

The -fpermissive option was added in Arduino AVR Boards 1.6.12 (which is bundled with Arduino IDE 1.6.10. The reason is they updated to a newer version of avr-gcc at that time, which upgraded some warnings to errors and Arduino didn't want to break code that previously worked. Unfortunately, -fpermissive also downgraded some things that had previously been errors to warnings as well:

Arduino SAMD Boards does not use -fpermissive in its compilation recipes, so the same code that will only generate a warning when compiled for an AVR board will generate an error when compiled for your Nano 33 IoT.

i've learned to add the -Wall and -Werror flags.

is there a way to specify these in the IDE?

In the Arduino IDE, you can enable warnings at File > Preferences > Compiler warnings. Although each hardware package can define them as the author chooses, in the official Arduino hardware packages (and many 3rd party packages as well), the "More" setting is -Wall and the "All" setting is -Wall -Wextra.

arduino-cli compile has a --warnings option that uses the same level names as the IDE.

The official Arduino hardware packages don't use -Werror, but the ESP32 core authors did use -Werror=all in the "More" and "All" warning levels. I think it was a good idea as a way to enforce higher code, but I have also seen it cause some suffering among beginners who turned warnings on, then encountered errors in code written by people who didn't bother to turn their warnings on.

You can add -Werror flags to any package by creating a file named platform.local.txt in the same folder as the package's platform.txt and adding this text to the file:

compiler.c.extra_flags=-Werror=all
compiler.c.elf.extra_flags=-Werror=all
compiler.cpp.extra_flags=-Werror=all

Interestingly, it doesn't upgrade the "jump to case label" ... "crosses initialization of" warning to an error though.

thanks for explaining