diff --git a/resources/subjects/tests/test_error/main.py b/resources/subjects/tests/test_error/main.py new file mode 100644 index 0000000..dd1624f --- /dev/null +++ b/resources/subjects/tests/test_error/main.py @@ -0,0 +1,21 @@ +import sys + + +def f(x: int): + if x < 0: + raise ValueError("x must be non-negative") + else: + return x + + +def g(x: int): + if x < 0: + return -x + else: + return x + + +if __name__ == "__main__": + x_ = int(sys.argv[1]) + g(x_) + f(x_) diff --git a/src/sflkit/analysis/factory.py b/src/sflkit/analysis/factory.py index 983ca87..6f2dd3e 100644 --- a/src/sflkit/analysis/factory.py +++ b/src/sflkit/analysis/factory.py @@ -18,6 +18,7 @@ ContainsDigitPredicate, ContainsSpecialPredicate, EmptyBytesPredicate, + FunctionErrorPredicate, ) from sflkit.analysis.spectra import Line, Function, Loop, DefUse, Length from sflkit.model.scope import Scope @@ -429,6 +430,29 @@ def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]: return self.objects[key][:] +class FunctionErrorFactory(AnalysisFactory): + def __init__(self): + super().__init__() + self.function_mapping = dict() + + def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]: + if event.event_type == EventType.FUNCTION_ENTER: + self.function_mapping[event.function_id] = event.line + if event.event_type in (EventType.FUNCTION_ERROR, EventType.FUNCTION_EXIT): + line = self.function_mapping[event.function_id] + key = ( + FunctionErrorPredicate.analysis_type(), + event.file, + line, + event.function_id, + ) + if key not in self.objects: + self.objects[key] = FunctionErrorPredicate( + event.file, line, event.function + ) + return [self.objects[key]] + + analysis_factory_mapping = { AnalysisType.LINE: LineFactory, AnalysisType.BRANCH: BranchFactory, @@ -446,4 +470,5 @@ def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]: AnalysisType.VARIABLE: VariableFactory, AnalysisType.SCALAR_PAIR: ScalarPairFactory, AnalysisType.FUNCTION: FunctionFactory, + AnalysisType.FUNCTION_ERROR: FunctionErrorFactory, } diff --git a/src/sflkit/analysis/mapping.py b/src/sflkit/analysis/mapping.py index a6bfccb..8765a1b 100644 --- a/src/sflkit/analysis/mapping.py +++ b/src/sflkit/analysis/mapping.py @@ -6,6 +6,7 @@ VariablePredicate, ReturnPredicate, NonePredicate, + FunctionErrorPredicate, ) from sflkit.analysis.predicate import ( EmptyStringPredicate, @@ -39,6 +40,7 @@ analysis_mapping[AnalysisType.DIGIT_STRING] = ContainsDigitPredicate analysis_mapping[AnalysisType.SPECIAL_STRING] = ContainsSpecialPredicate analysis_mapping[AnalysisType.CONDITION] = Condition +analysis_mapping[AnalysisType.FUNCTION_ERROR] = FunctionErrorPredicate """ If you want to add new spectra or predicates, please register them here and in sdtools/analysis/analysis_type.py diff --git a/src/sflkit/language/meta.py b/src/sflkit/language/meta.py index cd07c10..722b85a 100644 --- a/src/sflkit/language/meta.py +++ b/src/sflkit/language/meta.py @@ -61,7 +61,7 @@ def __init__(self): self.random = random.randbytes(4).hex() def get_var_name(self): - var = f"sd_tmp_{self.random}_{self._tmp_count}" + var = f"sk_tmp_{self.random}_{self._tmp_count}" self._tmp_count += 1 return var diff --git a/src/sflkit/language/python/factory.py b/src/sflkit/language/python/factory.py index 5fb86b9..66a0a79 100644 --- a/src/sflkit/language/python/factory.py +++ b/src/sflkit/language/python/factory.py @@ -521,7 +521,7 @@ def visit_function( function_error_event = FunctionErrorEvent( self.file, node.lineno, - self.get_function_event_id(node, self.event_id_generator), + self.event_id_generator.get_next_id(), node.name, self.get_function_id(node), ) diff --git a/src/sflkit/mapping.py b/src/sflkit/mapping.py index bb54263..8fd3260 100644 --- a/src/sflkit/mapping.py +++ b/src/sflkit/mapping.py @@ -27,12 +27,16 @@ def get_path(identifier: str): def load(config: Any): if not hasattr(config, "identifier"): raise InstrumentationError(f"Argument does not have an identifier") - file = EventMapping.get_path(config.identifier()) + return EventMapping.load_from_file(config.identifier()) + + @staticmethod + def load_from_file(identifier: str): + file = EventMapping.get_path(identifier) if file.exists(): return EventMapping(load_json(file)) else: raise InstrumentationError( - f"Cannot find information about instrumentation of {config.identifier()}" + f"Cannot find information about instrumentation of {identifier or file}" ) def write(self, config):