Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ck printer #1895

Merged
merged 47 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
201b4df
feat: halstead printer
devtooligan May 2, 2023
7eb270a
feat: halstead printer
devtooligan May 4, 2023
34a343e
feat: ck printer
devtooligan May 5, 2023
16b5726
feat: martin printer
devtooligan May 2, 2023
d682232
chore: add to scripts/ci_test_printers.sh
devtooligan May 11, 2023
6a0add0
Merge branch 'dev' into halstead
devtooligan Jun 14, 2023
1fa616b
Merge branch 'dev' into ck-printer
devtooligan Jun 14, 2023
314364e
chore: remove comments
devtooligan Jun 15, 2023
134dd90
Merge branch 'dev' into martin-printer
devtooligan Jun 15, 2023
975da91
chore: add type
devtooligan Jun 15, 2023
6f280c1
chore: pylint
devtooligan Jun 15, 2023
14c9761
feat: add CBO
devtooligan Jun 15, 2023
45f353b
Merge branch 'dev' into martin-printer
devtooligan Jun 15, 2023
fa22c34
chore: update label
devtooligan Jun 15, 2023
0fb6597
Merge branch 'martin-printer' into ck-printer
devtooligan Jun 15, 2023
3791467
docs: fix docstring
devtooligan Jun 16, 2023
c3a674a
chore: black
devtooligan Jun 16, 2023
a826bc3
Merge branch 'dev' into halstead
devtooligan Jun 16, 2023
c175d52
Merge branch 'dev' into halstead
devtooligan Jul 5, 2023
06e218c
refactor: prefer custom classes to dicts
devtooligan Jul 6, 2023
db5ec71
chore: move halstead utilities to utils folder
devtooligan Jul 6, 2023
0fb6e42
chore: lint
devtooligan Jul 6, 2023
46c6177
Merge branch 'dev' into halstead
devtooligan Jul 6, 2023
8f831ad
fix: 'type' object is not subscriptable
devtooligan Jul 6, 2023
61e3076
chore: lint
devtooligan Jul 6, 2023
fb25cbb
refactor: initial
devtooligan Jul 6, 2023
ce76d62
Merge branch 'halstead' into martin-printer
devtooligan Jul 6, 2023
8d2e7c4
refactor: prefer custom classes to dicts
devtooligan Jul 6, 2023
c787fb4
chore: move Martin logic to utils
devtooligan Jul 6, 2023
2e99e49
chore: lint
devtooligan Jul 6, 2023
8c51e47
Update scripts/ci_test_printers.sh
devtooligan Jul 7, 2023
5699349
fix: typo
devtooligan Jul 7, 2023
961a678
Merge branch 'halstead' into martin-printer
devtooligan Jul 7, 2023
4a3ab0a
fix: add martin printer to testing printer list
devtooligan Jul 7, 2023
524b755
Merge branch 'martin-printer' into ck-printer
devtooligan Jul 7, 2023
42cd6e0
fix: account for case w no functions
devtooligan Jul 7, 2023
251dee0
Merge branch 'halstead' into martin-printer
devtooligan Jul 7, 2023
6552ba0
Merge branch 'martin-printer' into ck-printer
devtooligan Jul 7, 2023
8d483ed
fix: external calls
devtooligan Jul 7, 2023
5c6cfb8
Merge branch 'martin-printer' into ck-printer
devtooligan Jul 7, 2023
6843d03
fix: external calls
devtooligan Jul 7, 2023
8a34cd4
Merge branch 'martin-printer' into ck-printer
devtooligan Jul 7, 2023
a35dff3
refactor: ck
devtooligan Jul 7, 2023
628f723
Merge branch 'dev' into ck-printer
montyly Sep 15, 2023
4053c9b
minor improvements
montyly Sep 15, 2023
c8bd72e
Fix circular dep
montyly Sep 15, 2023
bcbe4ff
Update ci_test_printers.sh
montyly Sep 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion scripts/ci_test_printers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
cd tests/e2e/solc_parsing/test_data/compile/ || exit

# Do not test the evm printer,as it needs a refactoring
ALL_PRINTERS="cfg,constructor-calls,contract-summary,data-dependency,echidna,function-id,function-summary,modifiers,call-graph,human-summary,inheritance,inheritance-graph,loc,slithir,slithir-ssa,vars-and-auth,require,variable-order,declaration"
ALL_PRINTERS="cfg,constructor-calls,contract-summary,data-dependency,echidna,function-id,function-summary,modifiers,call-graph,halstead,human-summary,inheritance,inheritance-graph,loc,martin,slithir,slithir-ssa,vars-and-auth,require,variable-order,declaration,ck"

# Only test 0.5.17 to limit test time
for file in *0.5.17-compact.zip; do
Expand Down
6 changes: 6 additions & 0 deletions slither/core/variables/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
from .state_variable import StateVariable
from .variable import Variable
from .local_variable_init_from_tuple import LocalVariableInitFromTuple
from .local_variable import LocalVariable
from .top_level_variable import TopLevelVariable
from .event_variable import EventVariable
from .function_type_variable import FunctionTypeVariable
from .structure_variable import StructureVariable
4 changes: 3 additions & 1 deletion slither/core/variables/local_variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from slither.core.variables.variable import Variable
from slither.core.solidity_types.user_defined_type import UserDefinedType
from slither.core.solidity_types.array_type import ArrayType
from slither.core.solidity_types.mapping_type import MappingType
from slither.core.solidity_types.elementary_type import ElementaryType

Expand Down Expand Up @@ -51,6 +50,9 @@ def is_storage(self) -> bool:
Returns:
(bool)
"""
# pylint: disable=import-outside-toplevel
from slither.core.solidity_types.array_type import ArrayType

if self.location == "memory":
return False
if self.location == "calldata":
Expand Down
3 changes: 3 additions & 0 deletions slither/printers/all_printers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from .summary.slithir import PrinterSlithIR
from .summary.slithir_ssa import PrinterSlithIRSSA
from .summary.human_summary import PrinterHumanSummary
from .summary.ck import CK
from .summary.halstead import Halstead
from .functions.cfg import CFG
from .summary.function_ids import FunctionIds
from .summary.variable_order import VariableOrder
Expand All @@ -21,3 +23,4 @@
from .summary.when_not_paused import PrinterWhenNotPaused
from .summary.declaration import Declaration
from .functions.dominator import Dominator
from .summary.martin import Martin
58 changes: 58 additions & 0 deletions slither/printers/summary/ck.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""
CK Metrics are a suite of six software metrics proposed by Chidamber and Kemerer in 1994.
These metrics are used to measure the complexity of a class.
https://en.wikipedia.org/wiki/Programming_complexity

- Response For a Class (RFC) is a metric that measures the number of unique method calls within a class.
- Number of Children (NOC) is a metric that measures the number of children a class has.
- Depth of Inheritance Tree (DIT) is a metric that measures the number of parent classes a class has.
- Coupling Between Object Classes (CBO) is a metric that measures the number of classes a class is coupled to.

Not implemented:
- Lack of Cohesion of Methods (LCOM) is a metric that measures the lack of cohesion in methods.
- Weighted Methods per Class (WMC) is a metric that measures the complexity of a class.

During the calculation of the metrics above, there are a number of other intermediate metrics that are calculated.
These are also included in the output:
- State variables: total number of state variables
- Constants: total number of constants
- Immutables: total number of immutables
- Public: total number of public functions
- External: total number of external functions
- Internal: total number of internal functions
- Private: total number of private functions
- Mutating: total number of state mutating functions
- View: total number of view functions
- Pure: total number of pure functions
- External mutating: total number of external mutating functions
- No auth or onlyOwner: total number of functions without auth or onlyOwner modifiers
- No modifiers: total number of functions without modifiers
- Ext calls: total number of external calls

"""
from slither.printers.abstract_printer import AbstractPrinter
from slither.utils.ck import CKMetrics
from slither.utils.output import Output


class CK(AbstractPrinter):
ARGUMENT = "ck"
HELP = "Chidamber and Kemerer (CK) complexity metrics and related function attributes"

WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#ck"

def output(self, _filename: str) -> Output:
if len(self.contracts) == 0:
return self.generate_output("No contract found")

ck = CKMetrics(self.contracts)

res = self.generate_output(ck.full_text)
res.add_pretty_table(ck.auxiliary1.pretty_table, ck.auxiliary1.title)
res.add_pretty_table(ck.auxiliary2.pretty_table, ck.auxiliary2.title)
res.add_pretty_table(ck.auxiliary3.pretty_table, ck.auxiliary3.title)
res.add_pretty_table(ck.auxiliary4.pretty_table, ck.auxiliary4.title)
res.add_pretty_table(ck.core.pretty_table, ck.core.title)
self.info(ck.full_text)

return res
49 changes: 49 additions & 0 deletions slither/printers/summary/halstead.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
Halstead complexity metrics
https://en.wikipedia.org/wiki/Halstead_complexity_measures

12 metrics based on the number of unique operators and operands:

Core metrics:
n1 = the number of distinct operators
n2 = the number of distinct operands
N1 = the total number of operators
N2 = the total number of operands

Extended metrics1:
n = n1 + n2 # Program vocabulary
N = N1 + N2 # Program length
S = n1 * log2(n1) + n2 * log2(n2) # Estimated program length
V = N * log2(n) # Volume

Extended metrics2:
D = (n1 / 2) * (N2 / n2) # Difficulty
E = D * V # Effort
T = E / 18 seconds # Time required to program
B = (E^(2/3)) / 3000 # Number of delivered bugs

"""
from slither.printers.abstract_printer import AbstractPrinter
from slither.utils.halstead import HalsteadMetrics
from slither.utils.output import Output


class Halstead(AbstractPrinter):
ARGUMENT = "halstead"
HELP = "Computes the Halstead complexity metrics for each contract"

WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#halstead"

def output(self, _filename: str) -> Output:
if len(self.contracts) == 0:
return self.generate_output("No contract found")

halstead = HalsteadMetrics(self.contracts)

res = self.generate_output(halstead.full_text)
res.add_pretty_table(halstead.core.pretty_table, halstead.core.title)
res.add_pretty_table(halstead.extended1.pretty_table, halstead.extended1.title)
res.add_pretty_table(halstead.extended2.pretty_table, halstead.extended2.title)
self.info(halstead.full_text)

return res
32 changes: 32 additions & 0 deletions slither/printers/summary/martin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""
Robert "Uncle Bob" Martin - Agile software metrics
https://en.wikipedia.org/wiki/Software_package_metrics

Efferent Coupling (Ce): Number of contracts that the contract depends on
Afferent Coupling (Ca): Number of contracts that depend on a contract
Instability (I): Ratio of efferent coupling to total coupling (Ce / (Ce + Ca))
Abstractness (A): Number of abstract contracts / total number of contracts
Distance from the Main Sequence (D): abs(A + I - 1)

"""
from slither.printers.abstract_printer import AbstractPrinter
from slither.utils.martin import MartinMetrics
from slither.utils.output import Output


class Martin(AbstractPrinter):
ARGUMENT = "martin"
HELP = "Martin agile software metrics (Ca, Ce, I, A, D)"

WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#martin"

def output(self, _filename: str) -> Output:
if len(self.contracts) == 0:
return self.generate_output("No contract found")

martin = MartinMetrics(self.contracts)

res = self.generate_output(martin.full_text)
res.add_pretty_table(martin.core.pretty_table, martin.core.title)
self.info(martin.full_text)
return res
Loading