|
17 | 17 | * defined in a super-class |
18 | 18 | */ |
19 | 19 |
|
20 | | -/* Need to find attributes defined in superclass (only in __init__?) */ |
21 | 20 | import python |
| 21 | +import semmle.python.ApiGraphs |
| 22 | +import semmle.python.dataflow.new.internal.DataFlowDispatch |
22 | 23 |
|
23 | | -predicate shadowed_by_super_class( |
24 | | - ClassObject c, ClassObject supercls, Assign assign, FunctionObject f |
| 24 | +predicate isSettableProperty(Function prop) { |
| 25 | + isProperty(prop) and |
| 26 | + exists(Function setter, DataFlow::AttrRead setterRead, FunctionExpr propExpr | |
| 27 | + setterRead.asExpr() = setter.getADecorator() and |
| 28 | + setterRead.getAttributeName() = "setter" and |
| 29 | + propExpr.getInnerScope() = prop and |
| 30 | + DataFlow::exprNode(propExpr).(DataFlow::LocalSourceNode).flowsTo(setterRead.getObject()) |
| 31 | + ) |
| 32 | +} |
| 33 | + |
| 34 | +predicate isProperty(Function prop) { |
| 35 | + prop.getADecorator() = API::builtin("property").asSource().asExpr() |
| 36 | +} |
| 37 | + |
| 38 | +predicate shadowedBySuperclass( |
| 39 | + Class cls, Class superclass, DataFlow::AttrWrite write, Function shadowed |
25 | 40 | ) { |
26 | | - c.getASuperType() = supercls and |
27 | | - c.declaredAttribute(_) = f and |
28 | | - exists(FunctionObject init, Attribute attr | |
29 | | - supercls.declaredAttribute("__init__") = init and |
30 | | - attr = assign.getATarget() and |
31 | | - attr.getObject().(Name).getId() = "self" and |
32 | | - attr.getName() = f.getName() and |
33 | | - assign.getScope() = init.getOrigin().(FunctionExpr).getInnerScope() |
| 41 | + getADirectSuperclass+(cls) = superclass and |
| 42 | + shadowed = cls.getAMethod() and |
| 43 | + exists(Function init | |
| 44 | + init = superclass.getInitMethod() and |
| 45 | + DataFlow::parameterNode(init.getArg(0)).(DataFlow::LocalSourceNode).flowsTo(write.getObject()) and |
| 46 | + write.getAttributeName() = shadowed.getName() |
34 | 47 | ) and |
35 | | - /* |
36 | | - * It's OK if the super class defines the method as well. |
37 | | - * We assume that the original method must have been defined for a reason. |
38 | | - */ |
39 | | - |
40 | | - not supercls.hasAttribute(f.getName()) |
| 48 | + // Allow cases in which the super class defines the method as well. |
| 49 | + // We assume that the original method must have been defined for a reason. |
| 50 | + not exists(Function superShadowed | |
| 51 | + superShadowed = superclass.getAMethod() and |
| 52 | + superShadowed.getName() = shadowed.getName() |
| 53 | + ) and |
| 54 | + // Allow properties if they have setters, as the write in the superclass will call the setter. |
| 55 | + not isSettableProperty(shadowed) |
41 | 56 | } |
42 | 57 |
|
43 | | -from ClassObject c, ClassObject supercls, Assign assign, FunctionObject shadowed |
44 | | -where shadowed_by_super_class(c, supercls, assign, shadowed) |
45 | | -select shadowed.getOrigin(), |
46 | | - "Method " + shadowed.getName() + " is shadowed by an $@ in super class '" + supercls.getName() + |
47 | | - "'.", assign, "attribute" |
| 58 | +from Class cls, Class superclass, DataFlow::AttrWrite write, Function shadowed, string extra |
| 59 | +where |
| 60 | + shadowedBySuperclass(cls, superclass, write, shadowed) and |
| 61 | + ( |
| 62 | + if isProperty(shadowed) |
| 63 | + then |
| 64 | + not isSettableProperty(shadowed) and |
| 65 | + extra = " (read-only property may cause an error if written to.)" |
| 66 | + else extra = "" |
| 67 | + ) |
| 68 | +select shadowed, "This method is shadowed by $@ in superclass $@." + extra, write, |
| 69 | + "attribute " + write.getAttributeName(), superclass, superclass.getName() |
0 commit comments