CodeQL queries 1.25
Skip to end of metadata
Go to start of metadata

Name: Redundant assignment

Description: Assigning a variable to itself is useless and very likely indicates an error in the code.

ID: py/redundant-assignment

Kind: problem

Severity: error

Precision: very-high

Query: RedundantAssignment.ql
/**
 * @name Redundant assignment
 * @description Assigning a variable to itself is useless and very likely indicates an error in the code.
 * @kind problem
 * @tags reliability
 *       useless-code
 *       external/cwe/cwe-563
 * @problem.severity error
 * @sub-severity low
 * @precision very-high
 * @id py/redundant-assignment
 */

import python

predicate assignment(AssignStmt a, Expr left, Expr right) {
  a.getATarget() = left and a.getValue() = right
}

predicate corresponding(Expr left, Expr right) {
  assignment(_, left, right)
  or
  exists(Attribute la, Attribute ra |
    corresponding(la, ra) and
    left = la.getObject() and
    right = ra.getObject()
  )
}

predicate same_value(Expr left, Expr right) {
  same_name(left, right)
  or
  same_attribute(left, right)
}

predicate maybe_defined_in_outer_scope(Name n) {
  exists(SsaVariable v | v.getAUse().getNode() = n | v.maybeUndefined())
}

/*
 * Protection against FPs in projects that offer compatibility between Python 2 and 3,
 * since many of them make assignments such as
 *
 * if PY2:
 *     bytes = str
 * else:
 *     bytes = bytes
 */

predicate isBuiltin(string name) { exists(Value v | v = Value::named(name) and v.isBuiltin()) }

predicate same_name(Name n1, Name n2) {
  corresponding(n1, n2) and
  n1.getVariable() = n2.getVariable() and
  not isBuiltin(n1.getId()) and
  not maybe_defined_in_outer_scope(n2)
}

ClassValue value_type(Attribute a) { a.getObject().pointsTo().getClass() = result }

predicate is_property_access(Attribute a) {
  value_type(a).lookup(a.getName()) instanceof PropertyValue
}

predicate same_attribute(Attribute a1, Attribute a2) {
  corresponding(a1, a2) and
  a1.getName() = a2.getName() and
  same_value(a1.getObject(), a2.getObject()) and
  exists(value_type(a1)) and
  not is_property_access(a1)
}

int pyflakes_commented_line(File file) {
  exists(Comment c | c.getText().toLowerCase().matches("%pyflakes%") |
    c.getLocation().hasLocationInfo(file.getAbsolutePath(), result, _, _, _)
  )
}

predicate pyflakes_commented(AssignStmt assignment) {
  exists(Location loc |
    assignment.getLocation() = loc and
    loc.getStartLine() = pyflakes_commented_line(loc.getFile())
  )
}

predicate side_effecting_lhs(Attribute lhs) {
  exists(ClassValue cls, ClassValue decl |
    lhs.getObject().pointsTo().getClass() = cls and
    decl = cls.getASuperType() and
    not decl.isBuiltin()
  |
    decl.declaresAttribute("__setattr__")
  )
}

from AssignStmt a, Expr left, Expr right
where
  assignment(a, left, right) and
  same_value(left, right) and
  // some people use self-assignment to shut Pyflakes up, such as `ok = ok # Pyflakes`
  not pyflakes_commented(a) and
  not side_effecting_lhs(left)
select a, "This assignment assigns a variable to itself."

Assigning a variable to itself is redundant and often an indication of a mistake in the code.

Recommendation

Check the assignment carefully for mistakes. If the assignment is truly redundant and not simply incorrect then remove it.

Example

In this example the programmer clearly intends to assign to self.eggs but made a mistake.

class Spam:

    def __init__(self, eggs):
        eggs = eggs

References