Coverage for analyzers / common.py: 100%

53 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-08 15:04 -0800

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

2 

3from __future__ import annotations 

4 

5from pathlib import Path 

6 

7from models import FunctionComplexity, FileComplexity 

8from common import ( 

9 _compute_loc, 

10 _compute_sloc, 

11 compute_maintainability_index, 

12 compute_ldi, 

13) 

14from metrics.duplication.core import ( 

15 compute_type1_hash, 

16 compute_type2_hash, 

17 count_statements, 

18) 

19 

20 

21def 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