https://github.com/akkartik/mu/blob/master/099hardware_checks.cc

     1 //: Let's raise errors when students use real hardware in any recipes besides
     2 //: 'main'. Part of the goal is to teach them testing hygiene and dependency
     3 //: injection.
     4 //:
     5 //: This is easy to sidestep, it's for feedback rather than safety.
     6
     7 :(before "End Globals")
     8 vector<type_tree*> Real_hardware_types;
     9 :(before "Begin transform_all")
    10 setup_real_hardware_types();
    11 :(before "End transform_all")
    12 teardown_real_hardware_types();
    13 :(code)
    14 void setup_real_hardware_types() {
    15   Real_hardware_types.push_back(parse_type("address:screen"));
    16   Real_hardware_types.push_back(parse_type("address:console"));
    17   Real_hardware_types.push_back(parse_type("address:resources"));
    18 }
    19 type_tree* parse_type(string s) {
    20   reagent x("x:"+s);
    21   type_tree* result = x.type;
    22   x.type = NULL;  // don't deallocate on return
    23   return result;
    24 }
    25 void teardown_real_hardware_types() {
    26   for (int i = 0;  i < SIZE(Real_hardware_types);  ++i)
    27     delete Real_hardware_types.at(i);
    28   Real_hardware_types.clear();
    29 }
    30
    31 :(before "End Checks")
    32 Transform.push_back(check_for_misuse_of_real_hardware);
    33 :(code)
    34 void check_for_misuse_of_real_hardware(const recipe_ordinal r) {
    35   const recipe& caller = get(Recipe, r);
    36   if (caller.name == "main") return;
    37   if (starts_with(caller.name, "scenario_")) return;
    38   trace(9991, "transform") << "--- check if recipe " << caller.name << " has any dependency-injection mistakes" << end();
    39   for (int index = 0;  index < SIZE(caller.steps);  ++index) {
    40     const instruction& inst = caller.steps.at(index);
    41     if (is_primitive(inst.operation)) continue;
    42     for (int i = 0;  i < SIZE(inst.ingredients);  ++i) {
    43       const reagent& ing = inst.ingredients.at(i);
    44       if (!is_literal(ing) || ing.name != "0") continue;
    45       const recipe& callee = get(Recipe, inst.operation);
    46       if (!callee.has_header) continue;
    47       if (i >= SIZE(callee.ingredients)) continue;
    48       const reagent& expected_ing = callee.ingredients.at(i);
    49       for (int j = 0;  j < SIZE(Real_hardware_types);  ++j) {
    50         if (*Real_hardware_types.at(j) == *expected_ing.type)
    51           raise << maybe(caller.name) << "'" << to_original_string(inst) << "': only 'main' can pass 0 into a " << to_string(expected_ing.type) << '\n' << end();
    52       }
    53     }
    54   }
    55 }
    56
    57 :(scenarios transform)
    58 :(scenario warn_on_using_real_screen_directly_in_non_main_recipe)
    59 % Hide_errors = true;
    60 def foo [
    61   print 0, 34
    62 ]
    63 +error: foo: 'print 0, 34': only 'main' can pass 0 into a (address screen)