Repository: crytic/slither

Issue:
Redundant inheritance lines in inheritance-graph printer
Hey guys! I've been using Slither for some time now and found it quite useful, so thanks for all the effort you put into developing this tool.

While using the `inheritance-graph` printer, I found that the resulting graph contains several redundant inheritance lines that should be better left off the graph. For instance: 
~~~solidity
pragma solidity ^0.4.25;
contract Grandparent { }
contract Parent is Grandparent {}
contract Child is Parent {}
~~~

Slither's printer output is:
![inheritance](https://user-images.githubusercontent.com/8171248/52140418-8188f400-2631-11e9-80bf-5466d8d86f8d.png)

Note that there's a line that goes from `Child` to `Grandparent` which is redundant, as `Child` is already `Parent` and `Parent` is already `Grandparent`. While this example is pretty simple, in far more complex inheritance chains these extra lines add little to no value and the graph ends up being too cluttered with lines pointing everywhere.

I'm not sure if this is a feature you added on purpose or a bug. If the former, are you considering adding an extra flag to the printer so those extra lines are not included in the final inheritance graph?.


Discussion:
Thank you @tinchoabbate for pointing out this limitation!

We can, indeed, print the inheritance without these additional lines. We can either replace the original printer or create a second one. 

I don't have a strong opinion, I will discuss it internally and see what makes the more sense.

Resolving patch:
diff --git a/slither/printers/inheritance/inheritance.py b/slither/printers/inheritance/inheritance.py
--- a/slither/printers/inheritance/inheritance.py
+++ b/slither/printers/inheritance/inheritance.py
@@ -31,16 +31,26 @@ def output(self, filename):
         if not self.contracts:
             return
 
-        info += blue('Child_Contract -> ') + green('Base_Contracts')
+        info += blue('Child_Contract -> ') + green('Immediate_Base_Contracts')
+        info += green(' [Not_Immediate_Base_Contracts]')
         for child in self.contracts:
             info += blue(f'\n+ {child.name}')
             if child.inheritance:
-                info += ' -> ' + green(", ".join(map(str, child.inheritance)))
-
-        info += green('\n\nBase_Contract -> ') + blue('Child_Contracts')
+                immediate = child.immediate_inheritance
+                not_immediate = [i for i in child.inheritance if i not in immediate]
+                info += ' -> ' + green(", ".join(map(str, immediate)))
+                if not_immediate:
+                    info += ", ["+ green(", ".join(map(str, not_immediate))) + "]"
+
+        info += green('\n\nBase_Contract -> ') + blue('Immediate_Child_Contracts')
+        info += blue(' [Not_Immediate_Child_Contracts]')
         for base in self.contracts:
             info += green(f'\n+ {base.name}')
             children = list(self._get_child_contracts(base))
             if children:
-                info += ' -> ' + blue(", ".join(map(str, children)))
+                immediate = [child for child in children if base in child.immediate_inheritance]
+                not_immediate = [child for child in children if not child in immediate]
+                info += ' -> ' + blue(", ".join(map(str, immediate)))
+                if not_immediate:
+                    info += ', [' + blue(", ".join(map(str, not_immediate))) + ']'
         self.info(info)
diff --git a/slither/printers/inheritance/inheritance_graph.py b/slither/printers/inheritance/inheritance_graph.py
--- a/slither/printers/inheritance/inheritance_graph.py
+++ b/slither/printers/inheritance/inheritance_graph.py
@@ -59,7 +59,7 @@ def _summary(self, contract):
         """
         ret = ''
         # Add arrows
-        for i in contract.inheritance:
+        for i in contract.immediate_inheritance:
             ret += '%s -> %s;\n' % (contract.name, i)
 
         # Functions


Test patch: