All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sonar.csharp.checks.ForLoopCounterChangedCheck Maven / Gradle / Ivy

/*
 * Sonar C# Plugin :: C# Squid :: Checks
 * Copyright (C) 2010 Jose Chillan, Alexandre Victoor and SonarSource
 * [email protected]
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package com.sonar.csharp.checks;

import com.google.common.collect.Sets;
import com.sonar.csharp.squid.api.CSharpPunctuator;
import com.sonar.csharp.squid.parser.CSharpGrammar;
import com.sonar.sslr.api.AstNode;
import com.sonar.sslr.api.GenericTokenType;
import com.sonar.sslr.api.Grammar;
import org.sonar.squidbridge.checks.SquidCheck;
import org.sonar.check.BelongsToProfile;
import org.sonar.check.Priority;
import org.sonar.check.Rule;

import javax.annotation.Nullable;
import java.util.Collections;
import java.util.Set;

@Rule(
  key = "S127",
  priority = Priority.MAJOR)
@BelongsToProfile(title = CheckList.SONAR_WAY_PROFILE, priority = Priority.MAJOR)
public class ForLoopCounterChangedCheck extends SquidCheck {

  private Set counters = Sets.newHashSet();
  private Set pendingCounters = Sets.newHashSet();

  @Override
  public void init() {
    subscribeTo(
      CSharpGrammar.FOR_STATEMENT,
      CSharpGrammar.STATEMENT,

      CSharpGrammar.ASSIGNMENT,
      CSharpPunctuator.INC_OP,
      CSharpPunctuator.DEC_OP);
  }

  @Override
  public void visitFile(@Nullable AstNode astNode) {
    counters.clear();
  }

  @Override
  public void visitNode(AstNode astNode) {
    if (astNode.is(CSharpGrammar.FOR_STATEMENT)) {
      pendingCounters = getLoopsCounters(astNode);
    } else if (astNode.is(CSharpGrammar.STATEMENT)) {
      counters.addAll(pendingCounters);
      pendingCounters = Collections.emptySet();
    } else if (!counters.isEmpty() && isAssignmentOrUnaryExpression(astNode)) {
      check(astNode);
    }
  }

  @Override
  public void leaveNode(AstNode astNode) {
    if (astNode.is(CSharpGrammar.FOR_STATEMENT)) {
      counters.removeAll(getLoopsCounters(astNode));
      pendingCounters = Collections.emptySet();
    }
  }

  private void check(AstNode node) {
    String modifiedVar;

    if (node.is(CSharpGrammar.ASSIGNMENT)) {
      modifiedVar = getAssignmentTargetName(node);
    } else if (isPostExpression(node)) {
      modifiedVar = getModifiedVarByPostfixOp(node);
    } else {
      // Prefix expr
      modifiedVar = node.getNextAstNode().getTokenValue();
    }

    if (modifiedVar != null && counters.contains(modifiedVar)) {
      reportIssue(node, modifiedVar);
    }
  }

  private void reportIssue(AstNode astNode, String counter) {
    getContext().createLineViolation(this, "Refactor the code to avoid updating the loop counter \"{0}\" within the loop body.", astNode, counter);
  }

  private Set getLoopsCounters(AstNode astNode) {
    Set counterList = Sets.newHashSet();
    AstNode initializer = astNode.getFirstChild(CSharpGrammar.FOR_INITIALIZER);

    if (initializer != null) {
      AstNode initializerChild = initializer.getFirstChild();

      if (initializerChild.is(CSharpGrammar.LOCAL_VARIABLE_DECLARATION)) {
        for (AstNode varDeclarator : initializerChild.getChildren(CSharpGrammar.LOCAL_VARIABLE_DECLARATOR)) {
          counterList.add(varDeclarator.getFirstChild(GenericTokenType.IDENTIFIER).getTokenValue());
        }
      } else {
        // Statement expression list
        for (AstNode expr : initializerChild.getChildren(CSharpGrammar.EXPRESSION)) {
          AstNode exprChild = expr.getFirstChild();
          if (exprChild.is(CSharpGrammar.ASSIGNMENT)) {
            counterList.add(getAssignmentTargetName(exprChild));
          }
        }
      }
    }
    return counterList;
  }

  private static String getAssignmentTargetName(AstNode assignment) {
    AstNode primaryExpression = assignment.getFirstChild(CSharpGrammar.ASSIGNMENT_TARGET).getFirstChild(CSharpGrammar.PRIMARY_EXPRESSION);
    String targetName = null;

    if (primaryExpression == null) {
      return targetName;
    }

    AstNode primaryChild = primaryExpression.getFirstChild();

    if (primaryChild.is(CSharpGrammar.SIMPLE_NAME)) {
      targetName = primaryExpression.getTokenValue();

    } else if (primaryChild.is(CSharpGrammar.POSTFIX_EXPRESSION)) {
      StringBuilder builder = new StringBuilder();

      for (AstNode varMember : primaryChild.getChildren()) {
        if (varMember.is(CSharpGrammar.SIMPLE_NAME)) {
          builder.append(varMember.getTokenValue());

        } else if (varMember.is(CSharpGrammar.POST_MEMBER_ACCESS)) {
          builder.append(varMember.getFirstChild(CSharpPunctuator.DOT).getTokenValue());
          builder.append(varMember.getFirstChild(GenericTokenType.IDENTIFIER).getTokenValue());

        } else if (varMember.is(CSharpGrammar.POST_ELEMENT_ACCESS)) {
          builder.append(CSharpPunctuator.LBRACKET);
          builder.append(varMember.getFirstChild(CSharpGrammar.ARGUMENT_LIST).getTokenValue());
          builder.append(CSharpPunctuator.RBRACKET);
        }
      }
      targetName = builder.toString();

    }
    return targetName;
  }

  private static String getModifiedVarByPostfixOp(AstNode unaryOperator) {
    AstNode parentPreviousNode = unaryOperator.getParent().getPreviousAstNode();

    if (parentPreviousNode.is(CSharpGrammar.SIMPLE_NAME)) {
      return parentPreviousNode.getTokenValue();
    }
    return null;
  }

  private static boolean isAssignmentOrUnaryExpression(AstNode node) {
    return node.is(CSharpGrammar.ASSIGNMENT, CSharpPunctuator.INC_OP, CSharpPunctuator.DEC_OP);
  }

  private static boolean isPostExpression(AstNode incOrDecOperator) {
    return incOrDecOperator.getParent().is(CSharpGrammar.POST_DECREMENT, CSharpGrammar.POST_INCREMENT);
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy