Skip to content

Enforce exhaustive enum switches at compile time: remove default: from exhaustive type-to-string switches #935

Description

@adsharma

Motivation

LogicalOperatorUtils::logicalOperatorTypeToString() threw Unknown logical operator type. at runtime on LSQB q9: the recent COUNT_ANTI_EDGE_CHAIN operator was added to the enum but a case was never added to the type-to-string switch.

-Wall -Wextra (which includes -Wswitch) would have caught this at compile time — but the switch ended in default: throw RuntimeException(...), and any default: label silences -Wswitch. The failure mode was converted from a build error into a runtime crash.

The same bug class exists in the sibling enum→string switches listed below. This was fixed for logicalOperatorTypeToString (see commit adding COUNT_ANTI_EDGE_CHAIN); this issue tracks applying the pattern to the rest.

The pattern

For switches that are exhaustive in intent (every enumerator returns / is handled):

// Before: -Wswitch silently disabled
switch (type) {
case Foo::A: return "A";
default: throw RuntimeException("Unknown type.");
}

// After: -Wswitch enforces coverage; missing enumerator = build failure with -Werror
switch (type) {
case Foo::A: return "A";
}
// No default label: -Wswitch then enforces that every enumerator is handled above.
throw RuntimeException("Unknown type.");  // guards only out-of-range values

Switches using default: UNREACHABLE_CODE; get the same treatment (replace the default label with the unreachable check after the switch).

Candidate files

Exhaustive-in-intent enum→string switches currently carrying a default: label:

  • src/common/enums/accumulate_type.cpp (1)
  • src/common/enums/conflict_action.cpp (1)
  • src/common/enums/drop_type.cpp (1)
  • src/common/enums/extend_direction_util.cpp (1)
  • src/common/enums/path_semantic.cpp (1)
  • src/common/enums/query_rel_type.cpp (2)
  • src/common/enums/rel_direction.cpp (2)
  • src/common/enums/rel_multiplicity.cpp (1)
  • src/common/enums/scan_source_type.cpp (1)
  • src/common/enums/table_type.cpp (1)
  • src/common/enums/transaction_action.cpp (1)
  • src/common/expression_type.cpp (2)
  • src/catalog/catalog_entry/catalog_entry_type.cpp (2)
  • src/processor/operator/physical_operator.cpp (1)
  • src/transaction/transaction_manager.cpp (1 — review; may be semantically non-exhaustive)

Out of scope

Not every switch with a default: should change:

  • Switches where default is semantically meaningful, e.g. LogicalOperatorUtils::isUpdate() returns false for everything else. -Wswitch forcing a case per new operator there would be noise.
  • Deserialize switches that intentionally tolerate unknown serialized values for forward compatibility.

Verification

With the pattern applied, temporarily commenting out any single case must produce warning: enumeration value 'X' not handled in switch [-Wswitch] — a hard failure under ENABLE_WERROR=ON (CI), and a visible warning in all other configs.

Notes

An X-macro generating both the enum and its string table would guarantee sync by construction, but is less readable/navigable; compiler enforcement via -Wswitch is preferred unless this class of bug recurs.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions