diff --git a/addons/cppcheckdata.py b/addons/cppcheckdata.py index 88a4d8cb157..bb759c46a89 100755 --- a/addons/cppcheckdata.py +++ b/addons/cppcheckdata.py @@ -349,6 +349,72 @@ class Token: linenr = None column = None + @classmethod + def from_token(cls, other_token): + """ + Create a new Token instance from an existing Token + """ + # Create an empty element-like dictionary with minimal required attributes + element = {'id': other_token.Id, 'str': other_token.str} + new_token = cls(element) + + # Copy basic attributes + new_token.scopeId = other_token.scopeId + new_token.scope = other_token.scope + new_token.type = other_token.type + new_token.isName = other_token.isName + new_token.isNumber = other_token.isNumber + new_token.isInt = other_token.isInt + new_token.isFloat = other_token.isFloat + new_token.isString = other_token.isString + new_token.strlen = other_token.strlen + new_token.isChar = other_token.isChar + new_token.isBoolean = other_token.isBoolean + new_token.isOp = other_token.isOp + new_token.isArithmeticalOp = other_token.isArithmeticalOp + new_token.isAssignmentOp = other_token.isAssignmentOp + new_token.isComparisonOp = other_token.isComparisonOp + new_token.isLogicalOp = other_token.isLogicalOp + new_token.isCast = other_token.isCast + new_token.isUnsigned = other_token.isUnsigned + new_token.isSigned = other_token.isSigned + new_token.externLang = other_token.externLang + new_token.macroName = other_token.macroName + new_token.isExpandedMacro = other_token.isExpandedMacro + new_token.isRemovedVoidParameter = other_token.isRemovedVoidParameter + new_token.isSplittedVarDeclComma = other_token.isSplittedVarDeclComma + new_token.isSplittedVarDeclEq = other_token.isSplittedVarDeclEq + new_token.isImplicitInt = other_token.isImplicitInt + new_token.isComplex = other_token.isComplex + new_token.isRestrict = other_token.isRestrict + new_token.isAttributeExport = other_token.isAttributeExport + new_token.linkId = other_token.linkId + new_token.link = other_token.link + new_token.varId = other_token.varId + new_token.variableId = other_token.variableId + new_token.variable = other_token.variable + new_token.functionId = other_token.functionId + new_token.function = other_token.function + new_token.valuesId = other_token.valuesId + new_token.values = other_token.values + new_token.valueType = other_token.valueType + new_token.typeScopeId = other_token.typeScopeId + new_token.typeScope = other_token.typeScope + new_token.astParentId = other_token.astParentId + new_token.astOperand1Id = other_token.astOperand1Id + new_token.astOperand1 = other_token.astOperand1 + new_token.astOperand2Id = other_token.astOperand2Id + new_token.astOperand2 = other_token.astOperand2 + new_token.originalName = other_token.originalName + new_token.astParent = other_token.astParent + + # Copy location information + new_token.file = other_token.file + new_token.linenr = other_token.linenr + new_token.column = other_token.column + + return new_token + def __init__(self, element): self.Id = element.get('id') self.str = element.get('str') diff --git a/addons/misra.py b/addons/misra.py index a80783d697e..eeac1654b66 100755 --- a/addons/misra.py +++ b/addons/misra.py @@ -621,6 +621,12 @@ def getEssentialTypeCategory(expr): # print('{0}: {1} {2}'.format(expr.str, e1, e2)) if e1 and e2 and e1 == e2: return e1 + # Addition for char Appendix D. + # If one operand is essentially character and the other is essentially signed or essentially unsigned + # then the essential type of the result is char; + if e1 and e2 and e1 == 'char' or e2 == 'char': + if e1 in ('signed', 'unsigned') or e2 in ('signed', 'unsigned'): + return 'char' if expr.valueType: return expr.valueType.sign if expr.valueType and expr.valueType.typeScope and expr.valueType.typeScope.className: @@ -661,7 +667,16 @@ def getEssentialTypeCategory(expr): return 'char' castTok = castTok.next + if expr.str == "~": + e1 = getEssentialTypeCategory(expr.astOperand1) + return e1 + if expr.valueType: + # For float and bool the sign is None and both are own categories + if expr.valueType.type == 'bool': + return 'bool' + if expr.valueType.type == 'float': + return "float" return expr.valueType.sign return None @@ -751,6 +766,14 @@ def getEssentialType(expr): e2 = getEssentialType(expr.astOperand2) if e1 is None or e2 is None: return None + # Addition for char Appendix D. + # If one operand is essentially character and the other is essentially signed or essentially unsigned + # then the essential type of the result is char; + if e1 == 'char' or e2 == 'char': + sign1 = e1.split(' ')[0] + sign2 = e2.split(' ')[0] + if sign1 in ('signed', 'unsigned') or sign2 in ('signed', 'unsigned'): + return 'char' if is_constant_integer_expression(expr): sign1 = e1.split(' ')[0] sign2 = e2.split(' ')[0] @@ -765,6 +788,17 @@ def getEssentialType(expr): elif expr.str == "~": e1 = getEssentialType(expr.astOperand1) return e1 + elif expr.str == "true" or expr.str == "false": + return 'bool' + elif expr.valueType is not None: + # First try to get smallest essential type based on the known Value of the expression + if expr.valueType.sign is not None and expr.valueType.type is not None: + e = get_essential_type_from_value(expr.getKnownIntValue(), expr.valueType.sign == 'signed') + if e: + return e + return (expr.valueType.sign + ' ' + expr.valueType.type) + if expr.valueType.type is not None: + return expr.valueType.type return None @@ -823,6 +857,8 @@ def is_constant_integer_expression(expr): return False if expr.isInt: return True + if expr.isCast and is_constant_integer_expression(expr.astOperand1): + return True if not expr.isArithmeticalOp: return False if expr.astOperand1 and not is_constant_integer_expression(expr.astOperand1): @@ -2464,29 +2500,125 @@ def isEssentiallyChar(op): self.reportError(token, 10, 2) def misra_10_3(self, cfg): - def get_category(essential_type): - if essential_type: - if essential_type in ('bool', 'char'): - return essential_type - if essential_type.split(' ')[-1] in ('float', 'double'): - return 'floating' - if essential_type.split(' ')[0] in ('unsigned', 'signed'): - return essential_type.split(' ')[0] - return None for tok in cfg.tokenlist: if tok.isAssignmentOp: lhs = getEssentialType(tok.astOperand1) - rhs = getEssentialType(tok.astOperand2) - #print(lhs) - #print(rhs) + # Handle Operation and Assignment Operators + if tok.str in ('+=', '-=', '*=', '/=', '%=', '&=', '|=', '^='): + # The += Token is like the + token and has both operand sides + # of the arithmetic operation + operatorToken = cppcheckdata.Token.from_token(tok) + operatorToken.isArithmeticalOp = True + operatorToken.isAssignmentOp = False + operatorToken.str = operatorToken.str.replace('=', '') + rhs = getEssentialType(operatorToken) + else: + rhs = getEssentialType(tok.astOperand2) if lhs is None or rhs is None: continue - lhs_category = get_category(lhs) - rhs_category = get_category(rhs) - if lhs_category and rhs_category and lhs_category != rhs_category and rhs_category not in ('signed','unsigned'): + lhs_category = getEssentialTypeCategory(tok.astOperand1) + # Handle Operation and Assignment Operators + if tok.str in ('+=', '-=', '*=', '/=', '%=', '&=', '|=', '^='): + # The += Token is like the + token and has both operand sides + # of the arithmetic operation + operatorToken = cppcheckdata.Token.from_token(tok) + operatorToken.str = operatorToken.str.replace('=', '') + operatorToken.isArithmeticalOp = True + operatorToken.isAssignmentOp = False + rhs_category = getEssentialTypeCategory(operatorToken) + else: + rhs_category = getEssentialTypeCategory(tok.astOperand2) + + if lhs_category and rhs_category and lhs_category != rhs_category: + # Exception: Catch enum on right and left hand side and send them to the size Check + # Exception: Catch enum<> on left hand side and send them to the size Check + # This is done to not throw errors while assigning the Enums Values (size then always matches) and + # Exception: Catch enum on right the right hand side as + # Anonymous Enums (in Misa Constant Enums) are allowed to be assigned if the size matches to a other category + if("enum= 0 ): + pass + # Backwards compatibility Exception: In C89, an integer constant expression of 0,1 or defines FALSE, TRUE may be assigned to a variable of type bool + elif( cfg.standards.c == 'c89' and lhs_category == "bool" and tok.astOperand2.str in ('0','1','FALSE','TRUE')): + continue + else: + self.reportError(tok, 10, 3) + continue + + if bitsOfEssentialType(lhs) < bitsOfEssentialType(rhs): self.reportError(tok, 10, 3) - if bitsOfEssentialType(lhs) < bitsOfEssentialType(rhs) and (lhs != "bool" or tok.astOperand2.str not in ('0','1')): + continue + elif isFunctionCall(tok, cfg.standards.c): + arguments = getArguments(tok) + if tok.astOperand1.function is None: + continue + for argnr, argvar in tok.astOperand1.function.argument.items(): + functionArgTok = argvar.nameToken + callArgTok = arguments[argnr - 1] + lhs = getEssentialType(functionArgTok) + rhs = getEssentialType(callArgTok) + + if lhs is None or rhs is None: + continue + + lhs_category = getEssentialTypeCategory(functionArgTok) + rhs_category = getEssentialTypeCategory(callArgTok) + + if lhs_category and rhs_category and lhs_category != rhs_category: + # Exception: Catch enum on right and left hand side and send them to the size Check + # Exception: Catch enum<> on left hand side and send them to the size Check + # This is done to not throw errors while assigning the Enums Values (size then always matches) and + # Exception: Catch enum on right the right hand side as + # Anonymous Enums (in Misa Constant Enums) are allowed to be assigned if the size matches to a other category + if("enum= 0 ): + pass + # Backwards compatibility Exception: In C89, an integer constant expression of 0,1 or defines FALSE, TRUE may be assigned to a variable of type bool + elif( cfg.standards.c == 'c89' and lhs_category == "bool" and tok.astOperand2.str in ('0','1','FALSE','TRUE')): + continue + else: + self.reportError(tok, 10, 3) + continue + + if bitsOfEssentialType(lhs) < bitsOfEssentialType(rhs): + self.reportError(tok, 10, 3) + continue + elif tok.str == "return" and tok.astOperand1 is not None: + lhs = getEssentialType(tok) + rhs = getEssentialType(tok.astOperand1) + if lhs is None or rhs is None: + continue + lhs_category = getEssentialTypeCategory(tok) + rhs_category = getEssentialTypeCategory(tok.astOperand1) + + if lhs_category and rhs_category and lhs_category != rhs_category: + # Exception: Catch enum on right and left hand side and send them to the size Check + # Exception: Catch enum<> on left hand side and send them to the size Check + # This is done to not throw errors while assigning the Enums Values (size then always matches) and + # Exception: Catch enum on right the right hand side as + # Anonymous Enums (in Misa Constant Enums) are allowed to be assigned if the size matches to a other category + if("enum= 0 ): + pass + # Backwards compatibility Exception: In C89, an integer constant expression of 0,1 or defines FALSE, TRUE may be assigned to a variable of type bool + elif( cfg.standards.c == 'c89' and lhs_category == "bool" and tok.astOperand2.str in ('0','1','FALSE','TRUE')): + continue + else: + self.reportError(tok, 10, 3) + continue + + if bitsOfEssentialType(lhs) < bitsOfEssentialType(rhs): # and (lhs != "bool" or (tok.astOperand2.str not in ('0','1') and cfg.standards.c == 'c89')): self.reportError(tok, 10, 3) + continue def misra_10_4(self, data): op = {'+', '-', '*', '/', '%', '&', '|', '^', '+=', '-=', ':'} diff --git a/addons/test/misra/misra-test.c b/addons/test/misra/misra-test.c index b289b5c48de..3881c89b306 100644 --- a/addons/test/misra/misra-test.c +++ b/addons/test/misra/misra-test.c @@ -96,7 +96,7 @@ static void misra_2_2(int x) { static void misra_2_7_unused_param (int *param1, int unused_param) // 2.7 { - *param1 = 42U; + *param1 = 42U; // 10.3 } static void misra_2_7_used_params (int *param1, int param2, int param3) @@ -306,8 +306,8 @@ struct misra_7_3_s static void misra_7_3(void) { long misra_7_3_a = 0l; //7.3 - long misra_7_3_b = 0lU; //7.3 - long long misra_7_3_c = 0Ull; //7.3 + long misra_7_3_b = 0lU; //7.3 10.3 + long long misra_7_3_c = 0Ull; //7.3 10.3 long long misra_7_3_d = 0ll; //7.3 long double misra_7_3_e = 7.3l; //7.3 struct misra_7_3_s misra_7_3_f = @@ -379,7 +379,7 @@ static int misra_8_2_o( const uint32_t a1, const uint8_t *const a2 ) -{ return *a2 + a1; } +{ return *a2 + a1; } // 10.3 static int misra_8_2_p( const uint32_t a1, const uint8_t *const a2 @@ -648,7 +648,7 @@ static void misra_10_1(uint32_t u, char c1, char c2, uint8_t u8) { char c; enum { E1 = 1 }; i = 3 << 1; // 10.1 - i = (u & u) << 4; // no-warning + i = (u & u) << 4; // 10.3 c = c1 & c2; // 10.1 c = c1 << 1; // 10.1 i = c1 > c2; // 10.3 @@ -679,7 +679,7 @@ static void misra_10_1_ternary(void) int8_t i8; int16_t i16; - a = ui16 << ui16; // 10.6 + a = ui16 << ui16; // 10.6 10.3 a = ui16 << (get_bool(42) ? ui16 : ui16); a = ui16 << (get_bool(42) ? ui16 : (get_bool(34) ? ui16 : ui16)); a = ui16 << (get_bool(42) ? (get_bool(34) ? ui16 : ui16) : ui16); @@ -699,7 +699,7 @@ static void misra_10_1_ternary(void) static void misra_10_2(void) { uint8_t u8a = 0; - char cha = 0; + char cha = 0; // 10.3 int8_t s8a = 0; int16_t s16a = 0; float f32a = 0.0; @@ -711,7 +711,7 @@ static void misra_10_2(void) { res = '0' - s8a; res = cha + ':'; // 10.2 - res = s16a - 'a'; // 10.2 10.3 10.4 + res = s16a - 'a'; // 10.2 10.4 res = '0' + f32a; // 10.2 10.4 // 10481 - crash @@ -729,6 +729,88 @@ static void misra_10_3(uint32_t u32a, uint32_t u32b) { bool b = true; // no-warning uint32_t u = UINT32_C(10); // no-warning } +enum { K1 = 1, K2 = 128 }; + +static uint8_t foo1 ( uint16_t x ) +{ + return x; // 10.3 +} + +void R_10_3 ( void ) // 8.4 +{ + uint8_t u8b = get_uint8 ( ); // no-warning + uint8_t u8c = get_uint8 ( ); // no-warning + uint8_t u8d = get_uint8 ( ); // no-warning + int8_t s8a; // no-warning + const uint8_t *pu8a; // no-warning + const uint8_t *pu8b = & u8b; // no-warning + + uint16_t u16a = get_uint16 ( ); // no-warning + uint32_t u32a; // no-warning + int32_t s32a = get_int32 ( ); // no-warning + + char cha = 'a'; // no-warning + + enum_t ena; // no-warning + + uint8_t u8a = 0; // no-warning + bool flag = ( bool_t ) 0; // no-warning + bool set = true; // no-warning + bool get = ( u8b > u8c ); // no-warning + use_bool ( flag ); // no-warning + use_bool ( get ); // no-warning + use_bool ( set ); // no-warning + use_uint8 ( u8a ); // no-warning + + ena = ENUM_1; + use_enum ( ena ); // no-warning + s8a = K1; // no-warning + use_int8 ( s8a ); // no-warning + u8a = 2; // no-warning + use_uint8( u8a ); // no-warning + u8a = 2 * 24; // no-warning + use_uint8( u8a ); // no-warning + cha += 1; // no-warning + use_char( cha ); // no-warning + + pu8a = pu8b; // no-warning + use_uint8( *pu8a ); // no-warning + u8a = u8b + u8c + u8d; // no-warning + use_uint8( u8a ); // no-warning + u8a = ( uint8_t ) s8a; // no-warning + use_uint8( u8a ); // no-warning + + u32a = u16a; // no-warning + use_uint32 ( u32a ); // no-warning + u32a = 2U + 125U; // no-warning + use_uint32 ( u32a ); // no-warning + use_uint16 ( u8a ); // no-warning + use_uint16 ( u8a + u16a ); // no-warning + + u8a = (uint16_t)2U + (uint16_t)3U; // no-warning + use_uint8( u8a ); + /** As we test in C89 this is no warning */ + bool_t bla = 0; // no-warning + + uint8_t u8f = 1.0f; // 10.3 + + cha = 7; // 10.3 + u8a = 'a'; // 10.3 + u8b = 1 - 2; // 10.3 + u8c += 'a'; // 10.3 + use_uint32 ( s32a ); // 10.3 + s8a = K2; // 10.3 + u16a = u32a; // 10.3 + use_uint16 ( u32a ); // 10.3 + s8a = -123L; // 10.3 + u8a = 6L; // 10.3 + for(uint16_t i = 0; i < 10U; i++) + { + u8a = (uint16_t)2U + i; // 10.3 + } + u16a = (uint16_t)50000U + (uint16_t)50000U; // 10.3 + u8a = (uint16_t)(2U + 3U); // 10.3 10.8 +} static void misra_10_4(u32 x, s32 y) { z = x + 3; // 10.4 @@ -977,10 +1059,10 @@ void misra_12_3(int a, int b, int c) { {} // No false positives in local and extern function calls - misra_12_3_fn4(misra_12_3_fn5(&a1, 32), &a1); - misra_12_3_fn4(misra_12_3_fn7(&a1, 32), &a1); - misra_12_3_fn6(misra_12_3_fn5(&a1, 32), &a1); - misra_12_3_fn6(misra_12_3_fn7(&a1, 32), &a1); + misra_12_3_fn4(misra_12_3_fn5(&a1, 32), &a1); // 10.3 + misra_12_3_fn4(misra_12_3_fn7(&a1, 32), &a1); // 10.3 + misra_12_3_fn6(misra_12_3_fn5(&a1, 32), &a1); // 10.3 + misra_12_3_fn6(misra_12_3_fn7(&a1, 32), &a1); // 10.3 misra_12_3_fn7(maxlen, fn(va, unsigned long), false); misra_12_3_fn8(maxlen, (unsigned long)((uintptr_t)fn(va, void*)), false); @@ -1214,9 +1296,9 @@ static void misra_13_5(void) { } static void misra_13_6(void) { - int a = sizeof(x|=42); // 13.6 - a = sizeof(--x); // 13.6 13.3 - return sizeof(x++); // 13.6 + int a = sizeof(x|=42); // 13.6 10.3 + a = sizeof(--x); // 13.6 13.3 10.3 + return sizeof(x++); // 13.6 10.3 } static void misra_14_1(void) { @@ -1389,7 +1471,7 @@ static void misra_14_2_fn2(void) i++; // 14.2 } - return 0; + return 0; // 10.3 } struct { @@ -1755,10 +1837,10 @@ static void misra_16_6(void) { // No 16 6 in this switch: switch (x) { - case A: return 1; // 15.5 - case B: return 1; // 15.5 - case C: return 1; // 15.5 - default: return 2; // 15.5 + case A: return 1; // 15.5 10.3 + case B: return 1; // 15.5 10.3 + case C: return 1; // 15.5 10.3 + default: return 2; // 15.5 10.3 } } @@ -1992,11 +2074,11 @@ static void misra_21_9(void) { static void misra_21_12(void) { int rc; fexcept_t f; // 21.12 - rc = feclearexcept(1); // 21.12 - rc = fegetexceptflag(&f, 1); // 21.12 - rc = feraiseexcept(1); // 21.12 - rc = fesetexceptflag(&f, 1); // 21.12 - rc = fetestexcept(1); // 21.12 + rc = feclearexcept(1); // 21.12 10.3 + rc = fegetexceptflag(&f, 1); // 21.12 10.3 + rc = feraiseexcept(1); // 21.12 10.3 + rc = fesetexceptflag(&f, 1); // 21.12 10.3 + rc = fetestexcept(1); // 21.12 10.3 } static void misra_21_14(uint8_t *x) { @@ -2006,7 +2088,7 @@ static void misra_21_14(uint8_t *x) { } static void misra_21_15(uint8_t *x, uint16_t *y) { - (void)memcpy(x, y, 10); // 21.15 + (void)memcpy(x, y, 10); // 21.15 10.3 (void)memmove(x, y, 10); // 21.15 (void)memcmp(x, y, 10); // 21.15 } @@ -2054,7 +2136,7 @@ static void misra_21_21(void) { } static void misra_22_5(FILE *f) { - int x = *f; // 22.5 + int x = *f; // 22.5 10.6 int y = f->pos; // 22.5 } diff --git a/addons/test/misra/misra-test.h b/addons/test/misra/misra-test.h index b455134893c..2a63955c71c 100644 --- a/addons/test/misra/misra-test.h +++ b/addons/test/misra/misra-test.h @@ -10,4 +10,78 @@ typedef struct m8_4_stErrorDef uint8_t ubReturnVal; } m8_4_stErrorDef; extern const m8_4_stErrorDef * m8_4_pubTestPointer; + + + +extern bool get_bool ( void ); +extern char_t get_char ( void ); +extern int8_t get_int8 ( void ); +extern int16_t get_int16 ( void ); +extern int32_t get_int32 ( void ); +extern int64_t get_int64 ( void ); +extern uint8_t get_uint8 ( void ); +extern uint16_t get_uint16 ( void ); +extern uint32_t get_uint32 ( void ); +extern uint64_t get_uint64 ( void ); +extern float32_t get_float32 ( void ); +extern float64_t get_float64 ( void ); +extern float128_t get_float128 ( void ); + +extern bool *get_bool_ptr ( void ); +extern char_t *get_char_ptr ( void ); +extern int8_t *get_int8_ptr ( void ); +extern int16_t *get_int16_ptr ( void ); +extern int32_t *get_int32_ptr ( void ); +extern int64_t *get_int64_ptr ( void ); +extern uint8_t *get_uint8_ptr ( void ); +extern uint16_t *get_uint16_ptr ( void ); +extern uint32_t *get_uint32_ptr ( void ); +extern uint64_t *get_uint64_ptr ( void ); +extern float32_t *get_float32_ptr ( void ); +extern float64_t *get_float64_ptr ( void ); +extern float128_t *get_float128_ptr ( void ); +extern enum_t get_enum ( void ); + +/* Functions that use a variable */ + +extern void use_bool ( bool use_bool_param ); +extern void use_char ( char_t use_char_param ); +extern void use_int8 ( int8_t use_int8_param ); +extern void use_int16 ( int16_t use_int16_param ); +extern void use_int32 ( int32_t use_int32_param ); +extern void use_int64 ( int64_t use_int64_param ); +extern void use_int128( int128_t use_int128_param ); +extern void use_uint8 ( uint8_t use_uint8_param ); +extern void use_uint16 ( uint16_t use_uint16_param ); +extern void use_uint32 ( uint32_t use_uint32_param ); +extern void use_uint64 ( uint64_t use_uint64_param ); +extern void use_uint128 ( uint128_t use_uint128_param ); +extern void use_float32 ( float32_t use_float32_param ); +extern void use_float64 ( float64_t use_float64_param ); +extern void use_float128 ( float128_t use_float128_param ); + +extern void use_void_ptr ( void * void_ptr_param ); +extern void use_bool_ptr ( bool *use_bool_ptr_param ); +extern void use_char_ptr ( char_t *use_char_ptr_param ); +extern void use_int8_ptr ( int8_t *use_int8_ptr_param ); +extern void use_int16_ptr ( int16_t *use_int16_ptr_param ); +extern void use_int32_ptr ( int32_t *use_int32_ptr_param ); +extern void use_int64_ptr ( int64_t *use_int64_ptr_param ); +extern void use_uint8_ptr ( uint8_t *use_uint8_ptr_param ); +extern void use_uint16_ptr ( uint16_t *use_uint16_ptr_param ); +extern void use_uint32_ptr ( uint32_t *use_uint32_ptr_param ); +extern void use_uint64_ptr ( uint64_t *use_uint64_ptr_param ); +extern void use_float32_ptr ( float32_t *use_float32_ptr_param ); +extern void use_float64_ptr ( float64_t *use_float64_ptr_param ); +extern void use_float128_ptr ( float128_t *use_float128_ptr_param ); + + +extern void use_const_char_ptr ( const char_t *use_c_char_ptr_param ); + + +extern void use_enum ( enum_t use_enum_param ); +extern void use_sizet ( size_t st ); +extern void use_ptrdiff ( ptrdiff_t pt ); + + #endif // MISRA_TEST_H