/**
 * @name Self-assignment
 * @description Assigning a variable to itself is useless and very likely indicates an error in the code.
 * @kind problem
 * @problem.severity error
 * @precision high
 * @id cs/self-assignment
 * @tags reliability
 *       correctness
 *       logic
 */

import csharp
import semmle.code.csharp.commons.StructuralComparison

class StructuralComparisonConfig extends StructuralComparisonConfiguration {
  StructuralComparisonConfig() {
    this = "SelfAssignment"
  }

  override predicate candidate(Element x, Element y) {
    exists(AssignExpr ae |
      // Member initializers are never self-assignments, in particular
      // not initializers such as `new C { F = F };`
      not ae instanceof MemberInitializer |
      ae.getLValue() = x
      and
      ae.getRValue() = y
    )
    and
    forall(Expr e |
      e = x.(Expr).getAChildExpr*() |
      // Non-trivial property accesses may have side-effects,
      // so these are not considered
      e instanceof PropertyAccess implies e instanceof TrivialPropertyAccess
    )
  }

  AssignExpr getSelfAssignExpr() {
    exists(Expr x, Expr y |
      same(x, y)
      and
      result.getLValue() = x
      and
      result.getRValue() = y
    )
  }
}

Declaration getDeclaration(Expr e) {
  result = e.(VariableAccess).getTarget()
  or
  result = e.(MemberAccess).getTarget()
  or
  result = getDeclaration(e.(ArrayAccess).getQualifier())
}

from StructuralComparisonConfig c, AssignExpr ae, Declaration target
where ae = c.getSelfAssignExpr() and target = getDeclaration(ae.getLValue())
select ae, "This assignment assigns $@ to itself.", target, target.getName()
