From 21a9de7d4276c8417e49607e20d091ab07a4a162 Mon Sep 17 00:00:00 2001 From: Maarten van der Schrieck Date: Thu, 4 Jan 2024 16:26:54 +0100 Subject: [PATCH] addons/namingng.py: Reinstate dict-with-regexps, cosmetic overhaul. (#5824) `namingng.py` somewhat supported specifying a dict instead of a list for regular expressions, until the feature was broken by a patch of mine recently. This PR contains a patch rewriting the feature and expanding relevant unit tests. To improve maintainability, a second patch is added that refactors the code for better readability and structure. --- addons/ROS_naming.json | 2 +- addons/namingng.py | 381 ++++++++++++++++++++--------------------- test/cli/test-other.py | 39 +++-- 3 files changed, 215 insertions(+), 207 deletions(-) diff --git a/addons/ROS_naming.json b/addons/ROS_naming.json index 82c0131cf..0262d69c0 100644 --- a/addons/ROS_naming.json +++ b/addons/ROS_naming.json @@ -1,5 +1,5 @@ { - "RE_FILE": {".*[A-Z]": [true, "under_scored"]}, + "RE_FILE": [".*[A-Z]"], "RE_NAMESPACE": {".*[A-Z]": [true, "under_scored"], ".*\\_$": [true, "under_scored"]}, "RE_FUNCTIONNAME": {".*\\_": [true, "camelCase"], diff --git a/addons/namingng.py b/addons/namingng.py index ad922c870..8cc2b8480 100755 --- a/addons/namingng.py +++ b/addons/namingng.py @@ -55,16 +55,35 @@ def configError(error,fatal=True): if fatal: sys.exit(1) +def validateConfigREs(list_or_dict,json_key): + have_error = False + for item in list_or_dict: + try: + re.compile(item) + except re.error as err: + configError("item '%s' of '%s' is not a valid regular expression: %s"%(item,json_key,err),fatal=False) + have_error = True + continue + if not isinstance(list_or_dict,dict): + continue + # item is actually a dict key; check value + value = list_or_dict[item] + if (not isinstance(value,list) or len(value) != 2 + or not isinstance(value[0],bool) or not isinstance(value[1],str)): + configError("item '%s' of '%s' must be an array [bool,string]"%(item,json_key),fatal=False) + have_error = True + + return have_error + def loadConfig(configfile): if not os.path.exists(configfile): configError("cannot find config file '%s'"%configfile) try: with open(configfile) as fh: - try: - data = json.load(fh) - except json.JSONDecodeError as e: - configError("error parsing config file as JSON at line %d: %s"%(e.lineno,e.msg)) + data = json.load(fh) + except json.JSONDecodeError as e: + configError("error parsing config file as JSON at line %d: %s"%(e.lineno,e.msg)) except Exception as e: configError("error opening config file '%s': %s"%(configfile,e)) @@ -82,18 +101,18 @@ def loadConfig(configfile): config = Config() mapping = { - 'file': ('RE_FILE', list), - 'namespace': ('RE_NAMESPACE', list), - 'include_guard': ('include_guard', dict), - 'variable': ('RE_VARNAME', list), - 'variable_prefixes': ('var_prefixes', dict, {}), - 'private_member': ('RE_PRIVATE_MEMBER_VARIABLE', list), - 'public_member': ('RE_PUBLIC_MEMBER_VARIABLE', list), - 'global_variable': ('RE_GLOBAL_VARNAME', list), - 'function_name': ('RE_FUNCTIONNAME', list), - 'function_prefixes': ('function_prefixes', dict, {}), - 'class_name': ('RE_CLASS_NAME', list), - 'skip_one_char_variables': ('skip_one_char_variables', bool), + 'file': ('RE_FILE', (list,)), + 'namespace': ('RE_NAMESPACE', (list,dict)), + 'include_guard': ('include_guard', (dict,)), + 'variable': ('RE_VARNAME', (list,dict)), + 'variable_prefixes': ('var_prefixes', (dict,), {}), + 'private_member': ('RE_PRIVATE_MEMBER_VARIABLE', (list,dict)), + 'public_member': ('RE_PUBLIC_MEMBER_VARIABLE', (list,dict)), + 'global_variable': ('RE_GLOBAL_VARNAME', (list,dict)), + 'function_name': ('RE_FUNCTIONNAME', (list,dict)), + 'function_prefixes': ('function_prefixes', (dict,), {}), + 'class_name': ('RE_CLASS_NAME', (list,dict)), + 'skip_one_char_variables': ('skip_one_char_variables', (bool,)), } # parse defined keys and store as members of config object @@ -103,23 +122,18 @@ def loadConfig(configfile): default = None if len(opts)<3 else opts[2] value = data.pop(json_key,default) - if value is not None and not isinstance(value, req_type): - req_typename = req_type.__name__ + if value is not None and type(value) not in req_type: + req_typename = ' or '.join([tp.__name__ for tp in req_type]) got_typename = type(value).__name__ configError('%s must be %s (not %s), or not set'%(json_key,req_typename,got_typename),fatal=False) have_error = True continue - if req_type == list and value is not None: - # type 'list' implies a list of regular expressions - for item in value: - try: - re.compile(item) - except re.error as err: - configError("item '%s' of '%s' is not a valid regular expression: %s"%(item,json_key,err),fatal=False) - have_error = True - if have_error: - continue + # type list implies that this is either a list of REs or a dict with RE keys + if list in req_type and value is not None: + re_error = validateConfigREs(value,json_key) + if re_error: + have_error = True setattr(config,key,value) @@ -135,34 +149,19 @@ def loadConfig(configfile): return config -def checkTrueRegex(data, expr, msg): - res = re.match(expr, data.str) - if res: - reportNamingError(data,msg) - - -def checkFalseRegex(data, expr, msg): - res = re.match(expr, data.str) - if not res: - reportNamingError(data,msg) - - def evalExpr(conf, exp, mockToken, msgType): - if isinstance(conf, dict): - if conf[exp][0]: - msg = msgType + ' ' + mockToken.str + ' violates naming convention : ' + conf[exp][1] - checkTrueRegex(mockToken, exp, msg) - elif ~conf[exp][0]: - msg = msgType + ' ' + mockToken.str + ' violates naming convention : ' + conf[exp][1] - checkFalseRegex(mockToken, exp, msg) - else: - msg = msgType + ' ' + mockToken.str + ' violates naming convention : ' + conf[exp][0] - checkFalseRegex(mockToken, exp, msg) - else: - msg = msgType + ' ' + mockToken.str + ' violates naming convention' - checkFalseRegex(mockToken, exp, msg) + report_as_error = False + msg = msgType + ' ' + mockToken.str + ' violates naming convention' -def check_include_guard_name(conf_include_guard,cfg,directive): + if isinstance(conf, dict): + report_as_error = conf[exp][0] + msg += ': ' + conf[exp][1] + + res = re.match(exp,mockToken.str) + if bool(res) == report_as_error: + reportNamingError(mockToken,msg) + +def check_include_guard_name(conf,directive): parts = directive.str.split() if len(parts) != 2: msg = 'syntax error' @@ -172,9 +171,9 @@ def check_include_guard_name(conf_include_guard,cfg,directive): guard_column = 1+directive.str.find(guard_name) filename = directive.file - if conf_include_guard.get('input','path') == 'basename': + if conf.include_guard.get('input','path') == 'basename': filename = os.path.basename(filename) - use_case = conf_include_guard.get('case','upper') + use_case = conf.include_guard.get('case','upper') if use_case == 'upper': filename = filename.upper() elif use_case == 'lower': @@ -186,20 +185,20 @@ def check_include_guard_name(conf_include_guard,cfg,directive): sys.exit(1) barename = re.sub('[^A-Za-z0-9]','_',filename).strip('_') - expect_guard_name = conf_include_guard.get('prefix','') + barename + conf_include_guard.get('suffix','') + expect_guard_name = conf.include_guard.get('prefix','') + barename + conf.include_guard.get('suffix','') if expect_guard_name != guard_name: msg = 'include guard naming violation; %s != %s'%(guard_name,expect_guard_name) reportNamingError(directive,msg,'includeGuardName',column=guard_column) return guard_name,guard_column -def check_include_guard(conf_include_guard,cfg,unguarded_include_files): +def check_include_guards(conf,cfg,unguarded_include_files): # Scan for '#ifndef FILE_H' as the first directive, in the first N lines. # Then test whether the next directive #defines the found name. # Various tests are done: # - check include guards for their naming and consistency # - test whether include guards are in place - max_linenr = conf_include_guard.get('max_linenr', 5) + max_linenr = conf.include_guard.get('max_linenr', 5) def report(directive,msg,errorId,column=0): reportNamingError(directive,msg,errorId,column=column) @@ -225,20 +224,19 @@ def check_include_guard(conf_include_guard,cfg,unguarded_include_files): continue if directive.linenr > max_linenr: - if phase == 0 and conf_include_guard.get('required',1): + if phase == 0 and conf.include_guard.get('required',1): report(directive,'include guard not found before line %d'%max_linenr,'includeGuardMissing') - pass phase = -1 continue if phase == 0: # looking for '#ifndef FILE_H' if not directive.str.startswith('#ifndef'): - if conf_include_guard.get('required',1): + if conf.include_guard.get('required',1): report(directive,'first preprocessor directive should be include guard #ifndef','includeGuardMissing') phase = -1 continue - guard_name,guard_column = check_include_guard_name(conf_include_guard,cfg,directive) + guard_name,guard_column = check_include_guard_name(conf,directive) if guard_name == None: phase = -1 continue @@ -265,7 +263,7 @@ def check_include_guard(conf_include_guard,cfg,unguarded_include_files): if pending_ifndef: report_pending_ifndef(pending_ifndef,guard_column) -def process(dumpfiles, configfile, debugprint=False): +def process(dumpfiles, configfile): conf = loadConfig(configfile) if conf.include_guard: @@ -278,140 +276,135 @@ def process(dumpfiles, configfile, debugprint=False): if not args.cli: print('Checking ' + afile + '...') data = cppcheckdata.CppcheckData(afile) + process_data(conf,data) - # Check File naming - if conf.file: - for source_file in data.files: - basename = os.path.basename(source_file) - good = False - for exp in conf.file: - good |= bool(re.match(exp, source_file)) - good |= bool(re.match(exp, basename)) - if not good: - mockToken = DataStruct(source_file, 0, basename) - reportNamingError(mockToken, 'File name ' + basename + ' violates naming convention') +def check_file_naming(conf,data): + for source_file in data.files: + basename = os.path.basename(source_file) + good = False + for exp in conf.file: + good |= bool(re.match(exp, source_file)) + good |= bool(re.match(exp, basename)) + if not good: + mockToken = DataStruct(source_file, 0, basename) + reportNamingError(mockToken, 'File name ' + basename + ' violates naming convention') - # Check Namespace naming - if conf.namespace: - for tk in data.rawTokens: - if tk.str == 'namespace': - mockToken = DataStruct(tk.next.file, tk.next.linenr, tk.next.str, tk.next.column) - msgType = 'Namespace' - for exp in conf.namespace: - evalExpr(conf.namespace, exp, mockToken, msgType) +def check_namespace_naming(conf,data): + for tk in data.rawTokens: + if tk.str != 'namespace': + continue + mockToken = DataStruct(tk.next.file, tk.next.linenr, tk.next.str, tk.next.column) + for exp in conf.namespace: + evalExpr(conf.namespace, exp, mockToken, 'Namespace') - unguarded_include_files = [] +def check_variable_naming(conf,cfg): + for var in cfg.variables: + if not var.nameToken: + continue + if var.access in ('Global','Public','Private'): + continue + prev = var.nameToken.previous + varType = prev.str + while "*" in varType and len(varType.replace("*", "")) == 0: + prev = prev.previous + varType = prev.str + varType + + if args.debugprint: + print("Variable Name: " + str(var.nameToken.str)) + print("original Type Name: " + str(var.nameToken.valueType.originalTypeName)) + print("Type Name: " + var.nameToken.valueType.type) + print("Sign: " + str(var.nameToken.valueType.sign)) + print("variable type: " + varType) + print("\n") + print("\t-- {} {}".format(varType, str(var.nameToken.str))) + + if conf.skip_one_char_variables and len(var.nameToken.str) == 1: + continue + if varType in conf.variable_prefixes: + prefix = conf.variable_prefixes[varType] + if not var.nameToken.str.startswith(prefix): + reportNamingError(var.typeStartToken, + 'Variable ' + + var.nameToken.str + + ' violates naming convention', + column=var.nameToken.column) + + mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str, var.nameToken.column) + for exp in conf.variable: + evalExpr(conf.variable, exp, mockToken, 'Variable') + +# Naming check for Global, Private and Public member variables +def check_gpp_naming(conf_list,cfg,access,message): + for var in cfg.variables: + if var.access != access: + continue + mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str, var.nameToken.column) + for exp in conf_list: + evalExpr(conf_list, exp, mockToken, message) + +def check_function_naming(conf,cfg): + for token in cfg.tokenlist: + if not token.function: + continue + if token.function.type in ('Constructor', 'Destructor', 'CopyConstructor', 'MoveConstructor'): + continue + retval = token.previous.str + prev = token.previous + while "*" in retval and len(retval.replace("*", "")) == 0: + prev = prev.previous + retval = prev.str + retval + if args.debugprint: + print("\t:: {} {}".format(retval, token.function.name)) + + if retval and retval in conf.function_prefixes: + if not token.function.name.startswith(conf.function_prefixes[retval]): + reportNamingError(token, 'Function ' + token.function.name + ' violates naming convention', column=token.column) + mockToken = DataStruct(token.file, token.linenr, token.function.name, token.column) + msgType = 'Function' + for exp in conf.function_name: + evalExpr(conf.function_name, exp, mockToken, msgType) + +def check_class_naming(conf,cfg): + for fnc in cfg.functions: + if fnc.type not in ('Constructor','Destructor'): + continue + mockToken = DataStruct(fnc.tokenDef.file, fnc.tokenDef.linenr, fnc.name, fnc.tokenDef.column) + msgType = 'Class ' + fnc.type + for exp in conf.class_name: + evalExpr(conf.class_name, exp, mockToken, msgType) + +def process_data(conf,data): + if conf.file: + check_file_naming(conf,data) + + if conf.namespace: + check_namespace_naming(conf,data) + + unguarded_include_files = [] + if conf.include_guard and conf.include_guard.get('required',1): + unguarded_include_files = [fn for fn in data.files if re.match(include_guard_header_re,fn)] + + for cfg in data.configurations: + if not args.cli: + print('Checking config %s...' % cfg.name) + if conf.variable: + check_variable_naming(conf,cfg) + if conf.private_member: + check_gpp_naming(conf.private_member,cfg,'Private','Private member variable') + if conf.public_member: + check_gpp_naming(conf.public_member,cfg,'Public','Public member variable') + if conf.global_variable: + check_gpp_naming(conf.global_variable,cfg,'Global','Global variable') + if conf.function_name: + check_function_naming(conf,cfg) + if conf.class_name: + check_class_naming(conf,cfg) if conf.include_guard: - if conf.include_guard.get('required',1): - unguarded_include_files = [fn for fn in data.files if re.match(include_guard_header_re,fn)] + check_include_guards(conf,cfg,unguarded_include_files) - for cfg in data.configurations: - if not args.cli: - print('Checking %s, config %s...' % (afile, cfg.name)) - if conf.variable: - for var in cfg.variables: - if var.nameToken and var.access != 'Global' and var.access != 'Public' and var.access != 'Private': - prev = var.nameToken.previous - varType = prev.str - while "*" in varType and len(varType.replace("*", "")) == 0: - prev = prev.previous - varType = prev.str + varType - - if debugprint: - print("Variable Name: " + str(var.nameToken.str)) - print("original Type Name: " + str(var.nameToken.valueType.originalTypeName)) - print("Type Name: " + var.nameToken.valueType.type) - print("Sign: " + str(var.nameToken.valueType.sign)) - print("variable type: " + varType) - print("\n") - print("\t-- {} {}".format(varType, str(var.nameToken.str))) - - if conf.skip_one_char_variables and len(var.nameToken.str) == 1: - continue - if varType in conf.variable_prefixes: - prefix = conf.variable_prefixes[varType] - if not var.nameToken.str.startswith(prefix): - reportNamingError(var.typeStartToken, - 'Variable ' + - var.nameToken.str + - ' violates naming convention', - column=var.nameToken.column) - - mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str, var.nameToken.column) - msgType = 'Variable' - for exp in conf.variable: - evalExpr(conf.variable, exp, mockToken, msgType) - - # Check Private Variable naming - if conf.private_member: - # TODO: Not converted yet - for var in cfg.variables: - if (var.access is None) or var.access != 'Private': - continue - mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str, var.nameToken.column) - msgType = 'Private member variable' - for exp in conf.private_member: - evalExpr(conf.private_member, exp, mockToken, msgType) - - # Check Public Member Variable naming - if conf.public_member: - for var in cfg.variables: - if (var.access is None) or var.access != 'Public': - continue - mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str, var.nameToken.column) - msgType = 'Public member variable' - for exp in conf.public_member: - evalExpr(conf.public_member, exp, mockToken, msgType) - - # Check Global Variable naming - if conf.global_variable: - for var in cfg.variables: - if (var.access is None) or var.access != 'Global': - continue - mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str, var.nameToken.column) - msgType = 'Global variable' - for exp in conf.global_variable: - evalExpr(conf.global_variable, exp, mockToken, msgType) - - # Check Functions naming - if conf.function_name: - for token in cfg.tokenlist: - if token.function: - if token.function.type in ('Constructor', 'Destructor', 'CopyConstructor', 'MoveConstructor'): - continue - retval = token.previous.str - prev = token.previous - while "*" in retval and len(retval.replace("*", "")) == 0: - prev = prev.previous - retval = prev.str + retval - if debugprint: - print("\t:: {} {}".format(retval, token.function.name)) - - if retval and retval in conf.function_prefixes: - if not token.function.name.startswith(conf.function_prefixes[retval]): - reportNamingError(token, 'Function ' + token.function.name + ' violates naming convention', column=token.column) - mockToken = DataStruct(token.file, token.linenr, token.function.name, token.column) - msgType = 'Function' - for exp in conf.function_name: - evalExpr(conf.function_name, exp, mockToken, msgType) - - # Check Class naming - if conf.class_name: - for fnc in cfg.functions: - # Check if it is Constructor/Destructor - if fnc.type == 'Constructor' or fnc.type == 'Destructor': - mockToken = DataStruct(fnc.tokenDef.file, fnc.tokenDef.linenr, fnc.name, fnc.tokenDef.column) - msgType = 'Class ' + fnc.type - for exp in conf.class_name: - evalExpr(conf.class_name, exp, mockToken, msgType) - - # Check include guard naming - if conf.include_guard: - check_include_guard(conf.include_guard,cfg,unguarded_include_files) - - for fn in unguarded_include_files: - mockToken = DataStruct(fn,0,os.path.basename(fn)) - reportNamingError(mockToken,'Missing include guard','includeGuardMissing') + for fn in unguarded_include_files: + mockToken = DataStruct(fn,0,os.path.basename(fn)) + reportNamingError(mockToken,'Missing include guard','includeGuardMissing') if __name__ == "__main__": parser = cppcheckdata.ArgumentParser() @@ -421,6 +414,6 @@ if __name__ == "__main__": help="Naming check config file") args = parser.parse_args() - process(args.dumpfile, args.configfile, args.debugprint) + process(args.dumpfile, args.configfile) sys.exit(0) diff --git a/test/cli/test-other.py b/test/cli/test-other.py index 1d67a634e..fdb67b992 100644 --- a/test/cli/test-other.py +++ b/test/cli/test-other.py @@ -347,7 +347,10 @@ def test_addon_namingng(tmpdir): "RE_NAMESPACE": ["[a-z][a-z0-9_]*[a-z0-9]\\Z"], "RE_VARNAME": ["[a-z][a-z0-9_]*[a-z0-9]\\Z"], "RE_PUBLIC_MEMBER_VARIABLE": ["[a-z][a-z0-9_]*[a-z0-9]\\Z"], - "RE_PRIVATE_MEMBER_VARIABLE": ["[a-z][a-z0-9_]*[a-z0-9]\\Z"], + "RE_PRIVATE_MEMBER_VARIABLE": { + ".*_tmp\\Z":[true,"illegal suffix _tmp"], + "priv_.*\\Z":[false,"required prefix priv_ missing"] + }, "RE_GLOBAL_VARNAME": ["[a-z][a-z0-9_]*[a-z0-9]\\Z"], "RE_FUNCTIONNAME": ["[a-z][a-z0-9_]*[a-z0-9]\\Z"], "include_guard": { @@ -410,10 +413,12 @@ static int _invalid_static_global; class _clz { public: - _clz() : _invalid_public(0), _invalid_private(0) { } + _clz() : _invalid_public(0), _invalid_private(0), priv_good(0), priv_bad_tmp(0) { } int _invalid_public; private: char _invalid_private; + int priv_good; + int priv_bad_tmp; }; namespace _invalid_namespace { } @@ -480,15 +485,18 @@ namespace _invalid_namespace { } 'static int _invalid_static_global;', ' ^', '{}:20:5: style: Class Constructor _clz violates naming convention [namingng-namingConvention]'.format(test_file), - ' _clz() : _invalid_public(0), _invalid_private(0) { }', + ' _clz() : _invalid_public(0), _invalid_private(0), priv_good(0), priv_bad_tmp(0) { }', ' ^', '{}:21:9: style: Public member variable _invalid_public violates naming convention [namingng-namingConvention]'.format(test_file), ' int _invalid_public;', ' ^', - '{}:23:10: style: Private member variable _invalid_private violates naming convention [namingng-namingConvention]'.format(test_file), + '{}:23:10: style: Private member variable _invalid_private violates naming convention: required prefix priv_ missing [namingng-namingConvention]'.format(test_file), ' char _invalid_private;', ' ^', - '{}:26:11: style: Namespace _invalid_namespace violates naming convention [namingng-namingConvention]'.format(test_file), + '{}:25:9: style: Private member variable priv_bad_tmp violates naming convention: illegal suffix _tmp [namingng-namingConvention]'.format(test_file), + ' int priv_bad_tmp;', + ' ^', + '{}:28:11: style: Namespace _invalid_namespace violates naming convention [namingng-namingConvention]'.format(test_file), 'namespace _invalid_namespace { }', ' ^', ] @@ -518,7 +526,12 @@ def test_addon_namingng_config(tmpdir): "RE_NAMESPACE": false, "RE_VARNAME": ["+bad pattern","[a-z]_good_pattern\\Z","(parentheses?"], "RE_PRIVATE_MEMBER_VARIABLE": "[a-z][a-z0-9_]*[a-z0-9]\\Z", - "RE_PUBLIC_MEMBER_VARIABLE": "[a-z][a-z0-9_]*[a-z0-9]\\Z", + "RE_PUBLIC_MEMBER_VARIABLE": { + "tmp_.*\\Z":[true,"illegal prefix tmp_"], + "bad_.*\\Z":true, + "public_.*\\Z":[false], + "pub_.*\\Z":[0,"required prefix pub_ missing"] + }, "RE_GLOBAL_VARNAME": "[a-z][a-z0-9_]*[a-z0-9]\\Z", "RE_FUNCTIONNAME": "[a-z][a-z0-9_]*[a-z0-9]\\Z", "RE_CLASS_NAME": "[a-z][a-z0-9_]*[a-z0-9]\\Z", @@ -561,17 +574,19 @@ def test_addon_namingng_config(tmpdir): assert lines == [ "Output:", "config error: RE_FILE must be list (not str), or not set", - "config error: RE_NAMESPACE must be list (not bool), or not set", + "config error: RE_NAMESPACE must be list or dict (not bool), or not set", "config error: include_guard must be dict (not bool), or not set", "config error: item '+bad pattern' of 'RE_VARNAME' is not a valid regular expression: nothing to repeat at position 0", "config error: item '(parentheses?' of 'RE_VARNAME' is not a valid regular expression: missing ), unterminated subpattern at position 0", "config error: var_prefixes must be dict (not list), or not set", - "config error: RE_PRIVATE_MEMBER_VARIABLE must be list (not str), or not set", - "config error: RE_PUBLIC_MEMBER_VARIABLE must be list (not str), or not set", - "config error: RE_GLOBAL_VARNAME must be list (not str), or not set", - "config error: RE_FUNCTIONNAME must be list (not str), or not set", + "config error: RE_PRIVATE_MEMBER_VARIABLE must be list or dict (not str), or not set", + "config error: item 'bad_.*\\Z' of 'RE_PUBLIC_MEMBER_VARIABLE' must be an array [bool,string]", + "config error: item 'public_.*\\Z' of 'RE_PUBLIC_MEMBER_VARIABLE' must be an array [bool,string]", + "config error: item 'pub_.*\\Z' of 'RE_PUBLIC_MEMBER_VARIABLE' must be an array [bool,string]", + "config error: RE_GLOBAL_VARNAME must be list or dict (not str), or not set", + "config error: RE_FUNCTIONNAME must be list or dict (not str), or not set", "config error: function_prefixes must be dict (not bool), or not set", - "config error: RE_CLASS_NAME must be list (not str), or not set", + "config error: RE_CLASS_NAME must be list or dict (not str), or not set", "config error: skip_one_char_variables must be bool (not str), or not set", "config error: unknown config key 'RE_VAR_NAME' [internalError]", "",