Appearance
| 1 | namespace Logging is | |
| 2 | use Collections.Iterable; | |
| 3 | use Collections.LIST; | |
| 4 | ||
| 5 | // The analysis-mode counter vocabulary. | |
| 6 | // | |
| 7 | // Analysis mode reports named counters over the STATS request, and a | |
| 8 | // test asserts against them that no more than the expected amount of | |
| 9 | // compilation took place. That only works if the names are declared | |
| 10 | // in one place rather than spelled at each bump site: `declare_all` | |
| 11 | // creates every counter up front, so a name shows up in the report at | |
| 12 | // count zero as well as when it fires, and a test can enumerate the | |
| 13 | // whole vocabulary and check each name is exercised somewhere. | |
| 14 | // | |
| 15 | // Three families: | |
| 16 | // | |
| 17 | // file-pass:<pass> one file taken through one pass of the | |
| 18 | // ordinary build loop, or through the | |
| 19 | // on-demand expression compile | |
| 20 | // file-rewalk:<pass> one file's bodies taken through one pass by | |
| 21 | // the incremental body re-walk | |
| 22 | // subtree-pass:<pass> one changed or appended subtree taken | |
| 23 | // through one pass | |
| 24 | // | |
| 25 | // The distinction is the point: an interface-preserving edit should | |
| 26 | // show body re-walks and no whole-file passes. | |
| 27 | class WORK_COUNTERS is | |
| 28 | EDIT_PATH_BODY_REWALK: string static => "edit-path-body-rewalk"; | |
| 29 | EDIT_PATH_INTERFACE_INCREMENTAL: string static => "edit-path-interface-incremental"; | |
| 30 | EDIT_PATH_FULL_REBUILD: string static => "edit-path-full-rebuild"; | |
| 31 | ||
| 32 | FILE_PASS_PREFIX: string static => "file-pass:"; | |
| 33 | FILE_REWALK_PREFIX: string static => "file-rewalk:"; | |
| 34 | SUBTREE_PASS_PREFIX: string static => "subtree-pass:"; | |
| 35 | DECLINED_PREFIX: string static => "edit-declined-"; | |
| 36 | ||
| 37 | // Why an EDIT was handled by the whole-project rebuild. Exactly | |
| 38 | // one of these is reported per rebuilt EDIT, so they sum to the | |
| 39 | // full-rebuild tally and the report says which guard was | |
| 40 | // responsible rather than only that some guard was. | |
| 41 | // | |
| 42 | // A guard inside the narrowing search declines a *route*, not the | |
| 43 | // edit: the search falls through to member replacement, which may | |
| 44 | // still succeed. So a reason is latched when its guard fires and | |
| 45 | // reported only if the edit ends up rebuilding. First latched | |
| 46 | // wins - the search rejects the narrowest route first and widens | |
| 47 | // as it falls through, so the first guard to fire is the one that | |
| 48 | // stopped the best route available. | |
| 49 | NOT_ELIGIBLE: string static => "not-eligible"; | |
| 50 | NO_RETAINED_PARSE: string static => "no-retained-parse"; | |
| 51 | MISSING_INTERFACE_SIGNATURE: string static => "missing-interface-signature"; | |
| 52 | FILE_ROOT_NOT_A_LIST: string static => "file-root-not-a-list"; | |
| 53 | FUNCTION_PAIRING_DESYNC: string static => "function-pairing-desync"; | |
| 54 | LOCATION_REFRESH_DESYNC: string static => "location-refresh-desync"; | |
| 55 | MULTIPLE_CHANGED_DEFINITIONS: string static => "multiple-changed-definitions"; | |
| 56 | FILE_LEVEL_CHANGE: string static => "file-level-change"; | |
| 57 | FEWER_DEFINITIONS: string static => "fewer-definitions"; | |
| 58 | RETAINED_PREFIX_MISMATCH: string static => "retained-prefix-mismatch"; | |
| 59 | NAMESPACE_NAME_MISMATCH: string static => "namespace-name-mismatch"; | |
| 60 | CLASS_HEADER_MISMATCH: string static => "class-header-mismatch"; | |
| 61 | NO_CLASS_SYMBOL: string static => "no-class-symbol"; | |
| 62 | UNRESETTABLE_CLOSURE: string static => "unresettable-closure"; | |
| 63 | OUTGOING_NOT_A_FUNCTION: string static => "outgoing-not-a-function"; | |
| 64 | OUTGOING_HAS_OVERRIDERS: string static => "outgoing-has-overriders"; | |
| 65 | OUTGOING_HAS_OVERRIDEES: string static => "outgoing-has-overridees"; | |
| 66 | CROSS_FILE_NAMESPACE_REFERENCE: string static => "cross-file-namespace-reference"; | |
| 67 | CROSS_FILE_UNKNOWN_FILE: string static => "cross-file-unknown-file"; | |
| 68 | INCOMING_NOT_A_FUNCTION: string static => "incoming-not-a-function"; | |
| 69 | ||
| 70 | edit_paths: Iterable[string] static => | |
| 71 | [ | |
| 72 | EDIT_PATH_BODY_REWALK, | |
| 73 | EDIT_PATH_INTERFACE_INCREMENTAL, | |
| 74 | EDIT_PATH_FULL_REBUILD | |
| 75 | ]; | |
| 76 | ||
| 77 | decline_reasons: Iterable[string] static => | |
| 78 | [ | |
| 79 | NOT_ELIGIBLE, | |
| 80 | NO_RETAINED_PARSE, | |
| 81 | MISSING_INTERFACE_SIGNATURE, | |
| 82 | FILE_ROOT_NOT_A_LIST, | |
| 83 | FUNCTION_PAIRING_DESYNC, | |
| 84 | LOCATION_REFRESH_DESYNC, | |
| 85 | MULTIPLE_CHANGED_DEFINITIONS, | |
| 86 | FILE_LEVEL_CHANGE, | |
| 87 | FEWER_DEFINITIONS, | |
| 88 | RETAINED_PREFIX_MISMATCH, | |
| 89 | NAMESPACE_NAME_MISMATCH, | |
| 90 | CLASS_HEADER_MISMATCH, | |
| 91 | NO_CLASS_SYMBOL, | |
| 92 | UNRESETTABLE_CLOSURE, | |
| 93 | OUTGOING_NOT_A_FUNCTION, | |
| 94 | OUTGOING_HAS_OVERRIDERS, | |
| 95 | OUTGOING_HAS_OVERRIDEES, | |
| 96 | CROSS_FILE_NAMESPACE_REFERENCE, | |
| 97 | CROSS_FILE_UNKNOWN_FILE, | |
| 98 | INCOMING_NOT_A_FUNCTION | |
| 99 | ]; | |
| 100 | ||
| 101 | // Create every declared counter so the report carries the whole | |
| 102 | // vocabulary, including names that have not fired. Called once at | |
| 103 | // analyser startup. | |
| 104 | declare_all(timers: TIMERS) static is | |
| 105 | for name in edit_paths do | |
| 106 | timers.declare(name); | |
| 107 | od | |
| 108 | ||
| 109 | for reason in decline_reasons do | |
| 110 | timers.declare(declined_name(reason)); | |
| 111 | od | |
| 112 | si | |
| 113 | ||
| 114 | declined_name(reason: string) -> string static => | |
| 115 | "{DECLINED_PREFIX}{reason}"; | |
| 116 | ||
| 117 | file_pass(timers: TIMERS, pass: string) static is | |
| 118 | timers.bump("{FILE_PASS_PREFIX}{pass}"); | |
| 119 | si | |
| 120 | ||
| 121 | file_rewalk(timers: TIMERS, pass: string) static is | |
| 122 | timers.bump("{FILE_REWALK_PREFIX}{pass}"); | |
| 123 | si | |
| 124 | ||
| 125 | subtree_pass(timers: TIMERS, pass: string) static is | |
| 126 | timers.bump("{SUBTREE_PASS_PREFIX}{pass}"); | |
| 127 | si | |
| 128 | ||
| 129 | declined(timers: TIMERS, reason: string) static is | |
| 130 | timers.bump(declined_name(reason)); | |
| 131 | si | |
| 132 | si | |
| 133 | si |