diff --git a/slither/tools/kspec_coverage/analysis.py b/slither/tools/kspec_coverage/analysis.py index b95e79f1f..6ca9f9a83 100755 --- a/slither/tools/kspec_coverage/analysis.py +++ b/slither/tools/kspec_coverage/analysis.py @@ -1,5 +1,8 @@ import re import logging + +from slither.core.declarations import Function +from slither.core.variables.variable import Variable from slither.utils.colors import yellow, green, red from slither.utils import json_utils @@ -46,13 +49,12 @@ def _get_all_covered_kspec_functions(target): def _get_slither_functions(slither): # Use contract == contract_declarer to avoid dupplicate all_functions_declared = [f for f in slither.functions if (f.contract == f.contract_declarer + and f.is_implemented and not f.is_constructor - and not f.is_empty and not f.is_constructor_variables)] # Use list(set()) because same state variable instances can be shared accross contracts # TODO: integrate state variables - # all_functions_declared += list(set([s for s in slither.state_variables if s.visibility in ['public', 'external']])) - # + all_functions_declared += list(set([s for s in slither.state_variables if s.visibility in ['public', 'external']])) slither_functions = {(function.contract.name, function.full_name): function for function in all_functions_declared} return slither_functions @@ -60,7 +62,7 @@ def _get_slither_functions(slither): def _generate_output(kspec, message, color, generate_json): info = "" for function in kspec: - info += f"{message} {function.contract.name}.{function.full_name}" + info += f"{message} {function.contract.name}.{function.full_name}\n" if info: logger.info(color(info)) @@ -74,7 +76,7 @@ def _generate_output(kspec, message, color, generate_json): def _generate_output_unresolved(kspec, message, color, generate_json): info = "" for contract, function in kspec: - info += f"{message} {contract}.{function}" + info += f"{message} {contract}.{function}\n" if info: logger.info(color(info)) @@ -84,7 +86,6 @@ def _generate_output_unresolved(kspec, message, color, generate_json): return None - def _run_coverage_analysis(args, slither, kspec_functions): # Collect all slither functions slither_functions = _get_slither_functions(slither) @@ -108,7 +109,14 @@ def _run_coverage_analysis(args, slither, kspec_functions): logger.info('## Check for functions coverage') json_kspec_present = _generate_output(kspec_present, "[✓]", green, args.json) - json_kspec_missing = _generate_output(kspec_missing, "[ ] (Missing)", red, args.json) + json_kspec_missing_functions = _generate_output([f for f in kspec_missing if isinstance(f, Function)], + "[ ] (Missing function)", + red, + args.json) + json_kspec_missing_variables = _generate_output([f for f in kspec_missing if isinstance(f, Variable)], + "[ ] (Missing variable)", + yellow, + args.json) json_kspec_unresolved = _generate_output_unresolved(kspec_functions_unresolved, "[ ] (Unresolved)", yellow, @@ -117,14 +125,21 @@ def _run_coverage_analysis(args, slither, kspec_functions): # Handle unresolved kspecs if args.json: json_utils.output_json(args.json, None, { - "kspec_present": json_kspec_present, - "kspec_missing": json_kspec_missing, - "kspec_unresolved": json_kspec_unresolved + "functions_present": json_kspec_present, + "functions_missing": json_kspec_missing_functions, + "variables_missing": json_kspec_missing_variables, + "functions_unresolved": json_kspec_unresolved }) def run_analysis(args, slither, kspec): # Get all of our kspec'd functions (tuple(contract_name, function_name)). - kspec_functions = _get_all_covered_kspec_functions(kspec) + if ',' in kspec: + kspecs = kspec.split(',') + kspec_functions = set() + for kspec in kspecs: + kspec_functions |= _get_all_covered_kspec_functions(kspec) + else: + kspec_functions = _get_all_covered_kspec_functions(kspec) # Run coverage analysis _run_coverage_analysis(args, slither, kspec_functions) diff --git a/slither/tools/kspec_coverage/kspec_coverage.py b/slither/tools/kspec_coverage/kspec_coverage.py index 43434a364..2ee25477f 100755 --- a/slither/tools/kspec_coverage/kspec_coverage.py +++ b/slither/tools/kspec_coverage/kspec_coverage.py @@ -6,7 +6,7 @@ def kspec_coverage(args): contract = args.contract kspec = args.kspec - slither = Slither(contract) + slither = Slither(contract, **vars(args)) # Run the analysis on the Klab specs run_analysis(args, slither, kspec)