slither.utils.loc

  1from dataclasses import dataclass, field
  2from pathlib import Path
  3from typing import List, Tuple
  4
  5from slither import Slither
  6from slither.utils.myprettytable import MyPrettyTable
  7from slither.utils.tests_pattern import is_test_file
  8
  9
 10@dataclass
 11class LoCInfo:
 12    loc: int = 0
 13    sloc: int = 0
 14    cloc: int = 0
 15
 16    def total(self) -> int:
 17        return self.loc + self.sloc + self.cloc
 18
 19
 20@dataclass
 21class LoC:
 22    src: LoCInfo = field(default_factory=LoCInfo)
 23    dep: LoCInfo = field(default_factory=LoCInfo)
 24    test: LoCInfo = field(default_factory=LoCInfo)
 25
 26    def to_pretty_table(self) -> MyPrettyTable:
 27        table = MyPrettyTable(["", "src", "dep", "test"])
 28
 29        table.add_row(["loc", str(self.src.loc), str(self.dep.loc), str(self.test.loc)])
 30        table.add_row(["sloc", str(self.src.sloc), str(self.dep.sloc), str(self.test.sloc)])
 31        table.add_row(["cloc", str(self.src.cloc), str(self.dep.cloc), str(self.test.cloc)])
 32        table.add_row(
 33            ["Total", str(self.src.total()), str(self.dep.total()), str(self.test.total())]
 34        )
 35        return table
 36
 37
 38def count_lines(contract_lines: List[str]) -> Tuple[int, int, int]:
 39    """Function to count and classify the lines of code in a contract.
 40    Args:
 41        contract_lines: list(str) representing the lines of a contract.
 42    Returns:
 43        tuple(int, int, int) representing (cloc, sloc, loc)
 44    """
 45    multiline_comment = False
 46    cloc = 0
 47    sloc = 0
 48    loc = 0
 49
 50    for line in contract_lines:
 51        loc += 1
 52        stripped_line = line.strip()
 53        if not multiline_comment:
 54            if stripped_line.startswith("//"):
 55                cloc += 1
 56            elif "/*" in stripped_line:
 57                # Account for case where /* is followed by */ on the same line.
 58                # If it is, then multiline_comment does not need to be set to True
 59                start_idx = stripped_line.find("/*")
 60                end_idx = stripped_line.find("*/", start_idx + 2)
 61                if end_idx == -1:
 62                    multiline_comment = True
 63                cloc += 1
 64            elif stripped_line:
 65                sloc += 1
 66        else:
 67            cloc += 1
 68            if "*/" in stripped_line:
 69                multiline_comment = False
 70
 71    return cloc, sloc, loc
 72
 73
 74def _update_lines(loc_info: LoCInfo, lines: list) -> None:
 75    """An internal function used to update (mutate in place) the loc_info.
 76
 77    Args:
 78        loc_info: LoCInfo to be updated
 79        lines: list(str) representing the lines of a contract.
 80    """
 81    cloc, sloc, loc = count_lines(lines)
 82    loc_info.loc += loc
 83    loc_info.cloc += cloc
 84    loc_info.sloc += sloc
 85
 86
 87def compute_loc_metrics(slither: Slither) -> LoC:
 88    """Used to compute the lines of code metrics for a Slither object.
 89
 90    Args:
 91        slither: A Slither object
 92    Returns:
 93        A LoC object
 94    """
 95
 96    loc = LoC()
 97
 98    for filename, source_code in slither.source_code.items():
 99        current_lines = source_code.splitlines()
100        is_dep = False
101        if slither.crytic_compile:
102            is_dep = slither.crytic_compile.is_dependency(filename)
103        loc_type = loc.dep if is_dep else loc.test if is_test_file(Path(filename)) else loc.src
104        _update_lines(loc_type, current_lines)
105    return loc
class LoCInfo:
12class LoCInfo:
13    loc: int = 0
14    sloc: int = 0
15    cloc: int = 0
16
17    def total(self) -> int:
18        return self.loc + self.sloc + self.cloc
LoCInfo(loc: int = 0, sloc: int = 0, cloc: int = 0)
loc: int = 0
sloc: int = 0
cloc: int = 0
def total(self) -> int:
17    def total(self) -> int:
18        return self.loc + self.sloc + self.cloc
class LoC:
22class LoC:
23    src: LoCInfo = field(default_factory=LoCInfo)
24    dep: LoCInfo = field(default_factory=LoCInfo)
25    test: LoCInfo = field(default_factory=LoCInfo)
26
27    def to_pretty_table(self) -> MyPrettyTable:
28        table = MyPrettyTable(["", "src", "dep", "test"])
29
30        table.add_row(["loc", str(self.src.loc), str(self.dep.loc), str(self.test.loc)])
31        table.add_row(["sloc", str(self.src.sloc), str(self.dep.sloc), str(self.test.sloc)])
32        table.add_row(["cloc", str(self.src.cloc), str(self.dep.cloc), str(self.test.cloc)])
33        table.add_row(
34            ["Total", str(self.src.total()), str(self.dep.total()), str(self.test.total())]
35        )
36        return table
LoC( src: LoCInfo = <factory>, dep: LoCInfo = <factory>, test: LoCInfo = <factory>)
src: LoCInfo
dep: LoCInfo
test: LoCInfo
def to_pretty_table(self) -> slither.utils.myprettytable.MyPrettyTable:
27    def to_pretty_table(self) -> MyPrettyTable:
28        table = MyPrettyTable(["", "src", "dep", "test"])
29
30        table.add_row(["loc", str(self.src.loc), str(self.dep.loc), str(self.test.loc)])
31        table.add_row(["sloc", str(self.src.sloc), str(self.dep.sloc), str(self.test.sloc)])
32        table.add_row(["cloc", str(self.src.cloc), str(self.dep.cloc), str(self.test.cloc)])
33        table.add_row(
34            ["Total", str(self.src.total()), str(self.dep.total()), str(self.test.total())]
35        )
36        return table
def count_lines(contract_lines: List[str]) -> Tuple[int, int, int]:
39def count_lines(contract_lines: List[str]) -> Tuple[int, int, int]:
40    """Function to count and classify the lines of code in a contract.
41    Args:
42        contract_lines: list(str) representing the lines of a contract.
43    Returns:
44        tuple(int, int, int) representing (cloc, sloc, loc)
45    """
46    multiline_comment = False
47    cloc = 0
48    sloc = 0
49    loc = 0
50
51    for line in contract_lines:
52        loc += 1
53        stripped_line = line.strip()
54        if not multiline_comment:
55            if stripped_line.startswith("//"):
56                cloc += 1
57            elif "/*" in stripped_line:
58                # Account for case where /* is followed by */ on the same line.
59                # If it is, then multiline_comment does not need to be set to True
60                start_idx = stripped_line.find("/*")
61                end_idx = stripped_line.find("*/", start_idx + 2)
62                if end_idx == -1:
63                    multiline_comment = True
64                cloc += 1
65            elif stripped_line:
66                sloc += 1
67        else:
68            cloc += 1
69            if "*/" in stripped_line:
70                multiline_comment = False
71
72    return cloc, sloc, loc

Function to count and classify the lines of code in a contract. Args: contract_lines: list(str) representing the lines of a contract. Returns: tuple(int, int, int) representing (cloc, sloc, loc)

def compute_loc_metrics(slither: slither.slither.Slither) -> LoC:
 88def compute_loc_metrics(slither: Slither) -> LoC:
 89    """Used to compute the lines of code metrics for a Slither object.
 90
 91    Args:
 92        slither: A Slither object
 93    Returns:
 94        A LoC object
 95    """
 96
 97    loc = LoC()
 98
 99    for filename, source_code in slither.source_code.items():
100        current_lines = source_code.splitlines()
101        is_dep = False
102        if slither.crytic_compile:
103            is_dep = slither.crytic_compile.is_dependency(filename)
104        loc_type = loc.dep if is_dep else loc.test if is_test_file(Path(filename)) else loc.src
105        _update_lines(loc_type, current_lines)
106    return loc

Used to compute the lines of code metrics for a Slither object.

Args: slither: A Slither object Returns: A LoC object