IMPALA-12668: Enable clang-tidy checks for implicit fallthrough

In switch/case statements, one case can fallthrough to the
next case. Sometimes this is intentional, but it is also a
common source of bugs (i.e. a missing break/return statement).
Clang-Tidy's clang-diagnostic-implicit-fallthrough flags
locations where a case falls through to the next case without
an explicit fallthrough declaration.

This change enables clang-diagnostic-implicit-fallthrough and
fixes failing locations. Since Impala uses C++17, this uses
C++17's [[fallthrough]] to indicate an explicit fallthrough.
This also adjusts clang-tidy's output to suggest [[fallthrough]]
as the preferred way to indicate fallthrough.

Testing:
 - Ran core job
 - Ran clang tidy

Change-Id: I6d65c92b442fa0317c3af228997571e124a54092
Reviewed-on: http://gerrit.cloudera.org:8080/20847
Tested-by: Impala Public Jenkins <impala-public-jenkins@cloudera.com>
Reviewed-by: Zihao Ye <eyizoha@163.com>
Reviewed-by: Michael Smith <michael.smith@cloudera.com>
This commit is contained in:
Joe McDonnell
2023-12-15 16:34:47 -08:00
committed by Michael Smith
parent 324a1aa37e
commit dd8ddf77c3
22 changed files with 105 additions and 32 deletions

View File

@@ -48,7 +48,6 @@ Checks: "-*,clang*,\
-clang-diagnostic-gnu-anonymous-struct,\ -clang-diagnostic-gnu-anonymous-struct,\
-clang-diagnostic-gnu-zero-variadic-macro-arguments,\ -clang-diagnostic-gnu-zero-variadic-macro-arguments,\
-clang-diagnostic-header-hygiene,\ -clang-diagnostic-header-hygiene,\
-clang-diagnostic-implicit-fallthrough,\
-clang-diagnostic-missing-prototypes,\ -clang-diagnostic-missing-prototypes,\
-clang-diagnostic-missing-variable-declarations,\ -clang-diagnostic-missing-variable-declarations,\
-clang-diagnostic-nested-anon-types,\ -clang-diagnostic-nested-anon-types,\

View File

@@ -91,7 +91,9 @@ inline int32_t AtoiUnsafe(const char* s, int len) {
bool negative = false; bool negative = false;
int i = 0; int i = 0;
switch (*s) { switch (*s) {
case '-': negative = true; case '-':
negative = true;
[[fallthrough]];
case '+': ++i; case '+': ++i;
} }
@@ -107,25 +109,34 @@ inline int32_t AtoiUnrolled(const char* s, int len) {
int32_t val = 0; int32_t val = 0;
bool negative = false; bool negative = false;
switch (*s) { switch (*s) {
case '-': negative = true; case '-':
negative = true;
[[fallthrough]];
case '+': --len; ++s; case '+': --len; ++s;
} }
switch(len) { switch(len) {
case 8: case 8:
val += (DIGIT(s[len - 8])) * 10000; val += (DIGIT(s[len - 8])) * 10000;
[[fallthrough]];
case 7: case 7:
val += (DIGIT(s[len - 7])) * 10000; val += (DIGIT(s[len - 7])) * 10000;
[[fallthrough]];
case 6: case 6:
val += (DIGIT(s[len - 6])) * 10000; val += (DIGIT(s[len - 6])) * 10000;
[[fallthrough]];
case 5: case 5:
val += (DIGIT(s[len - 5])) * 10000; val += (DIGIT(s[len - 5])) * 10000;
[[fallthrough]];
case 4: case 4:
val += (DIGIT(s[len - 4])) * 1000; val += (DIGIT(s[len - 4])) * 1000;
[[fallthrough]];
case 3: case 3:
val += (DIGIT(s[len - 3])) * 100; val += (DIGIT(s[len - 3])) * 100;
[[fallthrough]];
case 2: case 2:
val += (DIGIT(s[len - 2])) * 10; val += (DIGIT(s[len - 2])) * 10;
[[fallthrough]];
case 1: case 1:
val += (DIGIT(s[len - 1])); val += (DIGIT(s[len - 1]));
} }
@@ -140,7 +151,9 @@ inline int32_t AtoiCased(const char* s, int len) {
int32_t val = 0; int32_t val = 0;
bool negative = false; bool negative = false;
switch (*s) { switch (*s) {
case '-': negative = true; case '-':
negative = true;
[[fallthrough]];
case '+': --len; ++s; case '+': --len; ++s;
} }

View File

@@ -134,6 +134,7 @@ void CodegenAnyValReadWriteInfo::CodegenConvertToCanonicalForm() {
llvm::Value* new_val = CodegenAnyVal::ConvertToCanonicalForm(codegen_, builder_, llvm::Value* new_val = CodegenAnyVal::ConvertToCanonicalForm(codegen_, builder_,
type_, GetSimpleVal()); type_, GetSimpleVal());
SetSimpleVal(new_val); SetSimpleVal(new_val);
break;
} }
default: default:
; ;

View File

@@ -598,6 +598,7 @@ void CodegenAnyVal::ConvertToCanonicalForm() {
llvm::Value* new_val = ConvertToCanonicalForm(codegen_, llvm::Value* new_val = ConvertToCanonicalForm(codegen_,
builder_, type_, GetVal()); builder_, type_, GetVal());
SetVal(new_val); SetVal(new_val);
break;
} }
default: default:
; ;

View File

@@ -689,6 +689,7 @@ void HdfsOrcScanner::SetSyntheticAcidFieldForOriginalFile(const SlotDescriptor*
break; break;
case ACID_FIELD_ROWID_INDEX: case ACID_FIELD_ROWID_INDEX:
file_position_ = slot_desc; file_position_ = slot_desc;
break;
default: default:
break; break;
} }

View File

@@ -174,6 +174,7 @@ bool ColumnStatsReader::ReadFromString(StatsField stats_field,
static_cast<Decimal16Value*>(slot)); static_cast<Decimal16Value*>(slot));
} }
DCHECK(false) << "Unknown decimal byte size: " << col_type_.GetByteSize(); DCHECK(false) << "Unknown decimal byte size: " << col_type_.GetByteSize();
break;
case TYPE_DATE: case TYPE_DATE:
return ColumnStats<DateValue>::DecodePlainValue(encoded_value, slot, element_.type); return ColumnStats<DateValue>::DecodePlainValue(encoded_value, slot, element_.type);
default: default:

View File

@@ -260,7 +260,9 @@ void AggFnEvaluator::SetDstSlot(const AnyVal* src, const SlotDescriptor& dst_slo
#endif #endif
return; return;
default: default:
break; DCHECK(false) << "Unknown decimal byte size: "
<< dst_slot_desc.type().GetByteSize();
return;
} }
case TYPE_DATE: case TYPE_DATE:
*reinterpret_cast<DateValue*>(slot) = *reinterpret_cast<DateValue*>(slot) =

View File

@@ -330,7 +330,8 @@ class AnyValUtil {
#endif #endif
return; return;
default: default:
break; DCHECK(false) << "Unknown decimal byte size: " << type.GetByteSize();
return;
} }
case TYPE_DATE: case TYPE_DATE:
*reinterpret_cast<DateVal*>(dst) = *reinterpret_cast<DateVal*>(dst) =

View File

@@ -130,6 +130,7 @@ void ReportBadFormat(FunctionContext* context, FormatTokenizationResult error_ty
break; break;
case MISPLACED_FX_MODIFIER_ERROR: case MISPLACED_FX_MODIFIER_ERROR:
ss << "PARSE ERROR: FX modifier should be at the beginning of the format string."; ss << "PARSE ERROR: FX modifier should be at the beginning of the format string.";
break;
case CONFLICTING_DAY_OF_WEEK_TOKENS_ERROR: case CONFLICTING_DAY_OF_WEEK_TOKENS_ERROR:
ss << "PARSE ERROR: Multiple day of week tokens provided."; ss << "PARSE ERROR: Multiple day of week tokens provided.";
break; break;

View File

@@ -1011,6 +1011,7 @@ CodegenAnyValReadWriteInfo CodegenAnyValToReadWriteInfo(CodegenAnyVal& any_val,
case TYPE_STRUCT: case TYPE_STRUCT:
DCHECK(false) << "Invalid type for this function. " DCHECK(false) << "Invalid type for this function. "
<< "Call 'StoreStructToNativePtr()' instead."; << "Call 'StoreStructToNativePtr()' instead.";
break;
default: default:
DCHECK(false) << "NYI: " << rwi.type().DebugString(); DCHECK(false) << "NYI: " << rwi.type().DebugString();
break; break;
@@ -1074,6 +1075,7 @@ void SlotDescriptor::CodegenStoreNonNullAnyVal(
case TYPE_STRUCT: case TYPE_STRUCT:
DCHECK(false) << "Invalid type for this function. " DCHECK(false) << "Invalid type for this function. "
<< "Call 'StoreStructToNativePtr()' instead."; << "Call 'StoreStructToNativePtr()' instead.";
break;
default: default:
DCHECK(false) << "NYI: " << type.DebugString(); DCHECK(false) << "NYI: " << type.DebugString();
break; break;

View File

@@ -110,8 +110,10 @@ void MemBlock::Delete(bool* reserved, bool* allocated) {
free(data_); free(data_);
data_ = nullptr; data_ = nullptr;
*allocated = true; *allocated = true;
[[fallthrough]];
case MemBlockStatus::RESERVED: case MemBlockStatus::RESERVED:
*reserved = true; *reserved = true;
[[fallthrough]];
default: default:
SetStatusLocked(lock, MemBlockStatus::DISABLED); SetStatusLocked(lock, MemBlockStatus::DISABLED);
DCHECK(data_ == nullptr); DCHECK(data_ == nullptr);

View File

@@ -206,6 +206,7 @@ void RawValue::WriteNonNullPrimitive(const void* value, void* dst, const ColumnT
case TYPE_STRUCT: { case TYPE_STRUCT: {
// Structs should be handled by a different Write() function within this class. // Structs should be handled by a different Write() function within this class.
DCHECK(false); DCHECK(false);
break;
} }
default: default:
DCHECK(false) << "RawValue::WriteNonNullPrimitive(): bad type: " DCHECK(false) << "RawValue::WriteNonNullPrimitive(): bad type: "

View File

@@ -147,7 +147,8 @@ inline bool RawValue::Eq(const void* v1, const void* v2, const ColumnType& type)
return reinterpret_cast<const Decimal16Value*>(v1)->value() return reinterpret_cast<const Decimal16Value*>(v1)->value()
== reinterpret_cast<const Decimal16Value*>(v2)->value(); == reinterpret_cast<const Decimal16Value*>(v2)->value();
default: default:
break; DCHECK(false) << "Unknown decimal byte size: " << type.GetByteSize();
return 0;
} }
default: default:
DCHECK(false) << type; DCHECK(false) << type;

View File

@@ -37,12 +37,14 @@ bool IR_ALWAYS_INLINE RuntimeFilter::Eval(
return filter->EvalOverlap(col_type, val, val); return filter->EvalOverlap(col_type, val, val);
} }
} }
break;
} }
case TRuntimeFilterType::IN_LIST: { case TRuntimeFilterType::IN_LIST: {
InListFilter* filter = get_in_list_filter(); InListFilter* filter = get_in_list_filter();
if (LIKELY(filter && !filter->AlwaysTrue())) { if (LIKELY(filter && !filter->AlwaysTrue())) {
return filter->Find(val, col_type); return filter->Find(val, col_type);
} }
break;
} }
} }
return true; return true;

View File

@@ -146,6 +146,7 @@ TPrimitiveType::type ToThrift(PrimitiveType ptype) {
case TYPE_ARRAY: case TYPE_ARRAY:
case TYPE_MAP: case TYPE_MAP:
DCHECK(false) << "NYI: " << ptype; DCHECK(false) << "NYI: " << ptype;
[[fallthrough]];
default: return TPrimitiveType::INVALID_TYPE; default: return TPrimitiveType::INVALID_TYPE;
} }
} }

View File

@@ -472,6 +472,7 @@ int HS2ColumnarResultSet::AddRows(
to->binaryVal.values.insert(to->binaryVal.values.end(), to->binaryVal.values.insert(to->binaryVal.values.end(),
from->binaryVal.values.begin() + start_idx, from->binaryVal.values.begin() + start_idx,
from->binaryVal.values.begin() + start_idx + rows_added); from->binaryVal.values.begin() + start_idx + rows_added);
break;
default: default:
DCHECK(false) << "Unsupported type: " DCHECK(false) << "Unsupported type: "
<< TypeToString(ThriftToType( << TypeToString(ThriftToType(

View File

@@ -411,8 +411,10 @@ const uint8_t* BitPacking::UnpackUpTo31Values(const uint8_t* __restrict__ in,
#pragma push_macro("UNPACK_VALUES_CASE") #pragma push_macro("UNPACK_VALUES_CASE")
#define UNPACK_VALUES_CASE(ignore1, i, ignore2) \ #define UNPACK_VALUES_CASE(ignore1, i, ignore2) \
case 31 - i: out[30 - i] = \ case 31 - i: \
static_cast<OutType>(UnpackValue<BIT_WIDTH, 30 - i, false>(in_buffer)); out[30 - i] = \
static_cast<OutType>(UnpackValue<BIT_WIDTH, 30 - i, false>(in_buffer)); \
[[fallthrough]];
// Use switch with fall-through cases to minimise branching. // Use switch with fall-through cases to minimise branching.
switch (num_values) { switch (num_values) {
@@ -451,11 +453,14 @@ const uint8_t* BitPacking::UnpackAndDecodeUpTo31Values(const uint8_t* __restrict
#pragma push_macro("DECODE_VALUES_CASE") #pragma push_macro("DECODE_VALUES_CASE")
#define DECODE_VALUES_CASE(ignore1, i, ignore2) \ #define DECODE_VALUES_CASE(ignore1, i, ignore2) \
case 31 - i: { \ case 31 - i: \
uint32_t idx = UnpackValue<BIT_WIDTH, 30 - i, false>(in_buffer); \ { \
uint8_t* out_pos = reinterpret_cast<uint8_t*>(out) + (30 - i) * stride; \ uint32_t idx = UnpackValue<BIT_WIDTH, 30 - i, false>(in_buffer); \
DecodeValue(dict, dict_len, idx, reinterpret_cast<OutType*>(out_pos), decode_error); \ uint8_t* out_pos = reinterpret_cast<uint8_t*>(out) + (30 - i) * stride; \
} DecodeValue(dict, dict_len, idx, reinterpret_cast<OutType*>(out_pos), \
decode_error); \
} \
[[fallthrough]];
// Use switch with fall-through cases to minimise branching. // Use switch with fall-through cases to minimise branching.
switch (num_values) { switch (num_values) {

View File

@@ -140,14 +140,27 @@ class HashUtil {
const uint8_t* data2 = reinterpret_cast<const uint8_t*>(data); const uint8_t* data2 = reinterpret_cast<const uint8_t*>(data);
switch (len & 7) { switch (len & 7) {
case 7: h ^= uint64_t(data2[6]) << 48; case 7:
case 6: h ^= uint64_t(data2[5]) << 40; h ^= uint64_t(data2[6]) << 48;
case 5: h ^= uint64_t(data2[4]) << 32; [[fallthrough]];
case 4: h ^= uint64_t(data2[3]) << 24; case 6:
case 3: h ^= uint64_t(data2[2]) << 16; h ^= uint64_t(data2[5]) << 40;
case 2: h ^= uint64_t(data2[1]) << 8; [[fallthrough]];
case 1: h ^= uint64_t(data2[0]); case 5:
h *= MURMUR_PRIME; h ^= uint64_t(data2[4]) << 32;
[[fallthrough]];
case 4:
h ^= uint64_t(data2[3]) << 24;
[[fallthrough]];
case 3:
h ^= uint64_t(data2[2]) << 16;
[[fallthrough]];
case 2:
h ^= uint64_t(data2[1]) << 8;
[[fallthrough]];
case 1:
h ^= uint64_t(data2[0]);
h *= MURMUR_PRIME;
} }
h ^= h >> MURMUR_R; h ^= h >> MURMUR_R;
@@ -260,13 +273,26 @@ class HashUtil {
v = 0; v = 0;
switch (len & 7) { switch (len & 7) {
case 7: v ^= static_cast<uint64_t>(pos2[6]) << 48; case 7:
case 6: v ^= static_cast<uint64_t>(pos2[5]) << 40; v ^= static_cast<uint64_t>(pos2[6]) << 48;
case 5: v ^= static_cast<uint64_t>(pos2[4]) << 32; [[fallthrough]];
case 4: v ^= static_cast<uint64_t>(pos2[3]) << 24; case 6:
case 3: v ^= static_cast<uint64_t>(pos2[2]) << 16; v ^= static_cast<uint64_t>(pos2[5]) << 40;
case 2: v ^= static_cast<uint64_t>(pos2[1]) << 8; [[fallthrough]];
case 1: v ^= static_cast<uint64_t>(pos2[0]); case 5:
v ^= static_cast<uint64_t>(pos2[4]) << 32;
[[fallthrough]];
case 4:
v ^= static_cast<uint64_t>(pos2[3]) << 24;
[[fallthrough]];
case 3:
v ^= static_cast<uint64_t>(pos2[2]) << 16;
[[fallthrough]];
case 2:
v ^= static_cast<uint64_t>(pos2[1]) << 8;
[[fallthrough]];
case 1:
v ^= static_cast<uint64_t>(pos2[0]);
h ^= FastHashMix(v); h ^= FastHashMix(v);
h *= m; h *= m;
} }

View File

@@ -98,6 +98,7 @@ class JWKSetParser {
case rapidjson::kNumberType: case rapidjson::kNumberType:
if (value.IsInt()) return "Integer"; if (value.IsInt()) return "Integer";
if (value.IsDouble()) return "Float"; if (value.IsDouble()) return "Float";
[[fallthrough]];
default: default:
DCHECK(false); DCHECK(false);
return "Unknown"; return "Unknown";
@@ -908,4 +909,4 @@ Status JWTHelper::GetCustomClaimUsername(const JWTDecodedToken* decoded_token,
return status; return status;
} }
} // namespace impala } // namespace impala

View File

@@ -63,6 +63,7 @@ string NameOfTypeOfJsonValue(const Value& value) {
case rapidjson::kNumberType: case rapidjson::kNumberType:
if (value.IsInt()) return "Integer"; if (value.IsInt()) return "Integer";
if (value.IsDouble()) return "Float"; if (value.IsDouble()) return "Float";
[[fallthrough]];
default: default:
DCHECK(false); DCHECK(false);
return "Unknown"; return "Unknown";

View File

@@ -154,6 +154,7 @@ class StringParser {
switch (*s) { switch (*s) {
case '-': case '-':
is_negative = true; is_negative = true;
[[fallthrough]];
case '+': case '+':
++s; ++s;
--len; --len;
@@ -310,6 +311,7 @@ class StringParser {
case '-': case '-':
negative = true; negative = true;
max_val = static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1; max_val = static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1;
[[fallthrough]];
case '+': ++i; case '+': ++i;
} }
@@ -366,6 +368,7 @@ class StringParser {
case '-': case '-':
negative = true; negative = true;
max_val = static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1; max_val = static_cast<UnsignedT>(std::numeric_limits<T>::max()) + 1;
[[fallthrough]];
case '+': i = 1; case '+': i = 1;
} }
@@ -472,7 +475,9 @@ class StringParser {
bool negative = false; bool negative = false;
int i = 0; int i = 0;
switch (*s) { switch (*s) {
case '-': negative = true; // Fallthrough is intended. case '-':
negative = true;
[[fallthrough]];
case '+': i = 1; case '+': i = 1;
} }

View File

@@ -62,10 +62,15 @@ TMP_STDERR=$(mktemp)
STRCAT_MESSAGE="Impala-specific note: This can also be fixed using the StrCat() function \ STRCAT_MESSAGE="Impala-specific note: This can also be fixed using the StrCat() function \
from be/src/gutil/strings strcat.h)" from be/src/gutil/strings strcat.h)"
CLANG_STRING_CONCAT="performance-inefficient-string-concatenation" CLANG_STRING_CONCAT="performance-inefficient-string-concatenation"
FALLTHROUGH_MESSAGE="Impala-specific note: Impala is a C++ 17 codebase, so the preferred \
way to indicate intended fallthrough is C++ 17's [[fallthrough]]"
CLANG_FALLTHROUGH="clang-diagnostic-implicit-fallthrough"
trap "rm $TMP_STDERR" EXIT trap "rm $TMP_STDERR" EXIT
if ! run-clang-tidy.py -quiet -header-filter "${PIPE_DIRS%?}" \ if ! run-clang-tidy.py -quiet -header-filter "${PIPE_DIRS%?}" \
-j"${CORES}" ${DIRS} 2> ${TMP_STDERR} | \ -j"${CORES}" ${DIRS} 2> ${TMP_STDERR} | \
sed "/${CLANG_STRING_CONCAT}/ s#\$# ${STRCAT_MESSAGE}#"; sed "/${CLANG_STRING_CONCAT}/ s#\$# \n${STRCAT_MESSAGE}#" | \
sed "/${CLANG_FALLTHROUGH}/ s#\$# \n${FALLTHROUGH_MESSAGE}#" | \
sed 's#FALLTHROUGH_INTENDED#[[fallthrough]]#';
then then
echo "run-clang-tidy.py hit an error, dumping stderr output" echo "run-clang-tidy.py hit an error, dumping stderr output"
cat ${TMP_STDERR} >&2 cat ${TMP_STDERR} >&2