Coverage for analyzers/common.py: 100%

1"""Shared analysis logic for all language analyzers."""

2

3 from __future__ import annotations

4

5 from pathlib import Path

6

7 from models import FunctionComplexity, FileComplexity

8 from common import (

9 _compute_loc,

10 _compute_sloc,

11 compute_maintainability_index,

12 compute_ldi,

13)

14 from metrics.duplication.core import (

15 compute_type1_hash,

16 compute_type2_hash,

17 count_statements,

18)

19

20

21 def analyze_source_generic(

22 code,

23 parse_fn,

24 collect_functions_fn,

25 func_name_fn,

26 compute_cognitive_fn,

27 compute_halstead_fn,

28 compute_npath_fn,

29 compute_dtd_fn,

30 compute_cyclomatic_fn,

31 count_lloc_fn,

32 dup_config,

33 check_patterns_fn=None,

34 timing_manager=None,

35) -> FileComplexity:

36 """Shared analysis loop: parse, collect functions, compute all metrics."""

37 import perf

38

39 if timing_manager is None:

40 timing_manager = perf.create_timing_manager()

41

42 with perf.time_block() as t:

43 root = parse_fn(code)

44 functions = collect_functions_fn(root)

45 timing_manager["parse"].append(t[0])

46

47 with perf.time_block() as t:

48 result = FileComplexity(path="<string>")

49 result.loc = _compute_loc(code)

50 result.lloc = count_lloc_fn(root)

51 result.sloc = _compute_sloc(code, root)

52

53 if check_patterns_fn is not None:

54 result.pattern_violations = check_patterns_fn(root)

55 timing_manager["lloc"].append(t[0])

56

57 for func_node in functions:

58 name = func_name_fn(func_node)

59 line = func_node.start_point[0] + 1

60

61 with perf.time_block() as t:

62 complexity = compute_cognitive_fn(func_node, name)

63 timing_manager["cognitive"].append(t[0])

64

65 with perf.time_block() as t:

66 halstead = compute_halstead_fn(func_node)

67 timing_manager["halstead"].append(t[0])

68

69 with perf.time_block() as t:

70 npath = compute_npath_fn(func_node)

71 timing_manager["npath"].append(t[0])

72

73 with perf.time_block() as t:

74 dtd = compute_dtd_fn(func_node)

75 timing_manager["dtd"].append(t[0])

76

77 with perf.time_block() as t:

78 cyclomatic = compute_cyclomatic_fn(func_node)

79 timing_manager["cyclomatic"].append(t[0])

80

81 func_loc = func_node.end_point[0] - func_node.start_point[0] + 1

82

83 with perf.time_block() as t:

84 mi = compute_maintainability_index(halstead.volume, cyclomatic, func_loc)

85 timing_manager["mi"].append(t[0])

86

87 with perf.time_block() as t:

88 ldi = compute_ldi(

89 halstead.volume, halstead.vocabulary, halstead.n2, halstead.difficulty

90 )

91 timing_manager["ldi"].append(t[0])

92

93 with perf.time_block() as t:

94 t1 = compute_type1_hash(func_node, dup_config)

95 t2 = compute_type2_hash(func_node, dup_config)

96 stmts = count_statements(func_node)

97 timing_manager["duplication"].append(t[0])

98

99 result.functions.append(

100 FunctionComplexity(

101 name=name,

102 line=line,

103 complexity=complexity,

104 halstead=halstead,

105 npath=npath,

106 dtd=dtd,

107 mi=mi,

108 ldi=ldi,

109 cyclomatic=cyclomatic,

110 type1_hash=t1,

111 type2_hash=t2,

112 func_loc=func_loc,

113 stmt_count=stmts,

114 )

115 )

116 return result