001/*-------------------------------------------------------------------------+ 002| | 003| Copyright (c) 2009-2018 CQSE GmbH | 004| | 005+-------------------------------------------------------------------------*/ 006package eu.cqse.check.base; 007 008import static eu.cqse.check.framework.scanner.ETokenType.IDENTIFIER; 009 010import java.util.EnumSet; 011import java.util.List; 012import java.util.Set; 013 014import eu.cqse.check.framework.core.CheckException; 015import eu.cqse.check.framework.scanner.ELanguage; 016import eu.cqse.check.framework.scanner.ETokenType; 017import eu.cqse.check.framework.scanner.IToken; 018import eu.cqse.check.framework.shallowparser.framework.ShallowEntity; 019 020/** 021 * Base class for checks that analyze comparison expressions. This handles only 022 * the very basic case of identifier compared to itself. 023 */ 024public abstract class AvoidObjectComparisonWithSelfCheckBase extends EntityCheckBase { 025 026 private static final Set<ETokenType> BOOLEAN_CONNECTIVES = EnumSet.of(ETokenType.ANDAND, ETokenType.OROR); 027 028 /** {@inheritDoc} */ 029 @Override 030 protected String getXPathSelectionString() { 031 return "//STATEMENT"; 032 } 033 034 /** {@inheritDoc} */ 035 @Override 036 protected void processEntity(ShallowEntity entity) throws CheckException { 037 List<IToken> tokens = entity.ownStartTokens(); 038 for (int index : getIndicesOfComparisonExpression(tokens)) { 039 checkOperands(tokens, index); 040 } 041 } 042 043 /** Checks whether operands of comparison are the same. */ 044 private void checkOperands(List<IToken> tokens, int index) throws CheckException { 045 int secondOperandIndex = getSecondOperandIndex(index); 046 if (index <= 1 || secondOperandIndex + 1 >= tokens.size()) { 047 return; 048 } 049 050 IToken firstOperand = tokens.get(index - 1); 051 IToken secondOperand = tokens.get(secondOperandIndex); 052 053 // we handle only single identifiers 054 if (firstOperand.getType() != IDENTIFIER || secondOperand.getType() != IDENTIFIER) { 055 return; 056 } 057 058 // if there is something complex (anything besides the opening parenthesis and 059 // && or ||) before, we rather skip this, to avoid false positives (might be 060 // lots of complex expressions) 061 ETokenType tokenBefore = tokens.get(index - 2).getType(); 062 if (tokenBefore != ETokenType.LPAREN && !BOOLEAN_CONNECTIVES.contains(tokenBefore)) { 063 if (!(context.getLanguage() == ELanguage.OBJECTIVE_C && tokenBefore == ETokenType.LBRACK)) { 064 return; 065 } 066 } 067 068 // if there is something complex (anything besides the closing parenthesis and 069 // && or ||) after, we rather skip this, to avoid false positives (might be lots 070 // of complex expressions) 071 ETokenType tokenAfter = tokens.get(secondOperandIndex + 1).getType(); 072 if (tokenAfter != ETokenType.RPAREN && !BOOLEAN_CONNECTIVES.contains(tokenAfter)) { 073 if (!(context.getLanguage() == ELanguage.OBJECTIVE_C && tokenAfter == ETokenType.RBRACK)) { 074 return; 075 } 076 } 077 078 if (firstOperand.getText().equals(secondOperand.getText()) && !isRuleException(firstOperand)) { 079 createFinding("`" + firstOperand.getText() + "` is compared with itself", firstOperand, secondOperand); 080 } 081 } 082 083 /** 084 * Template method that can return true, if the given operand is an exception 085 * from the rule. 086 * 087 * @param operand 088 * the operand to be checked for exception case. 089 * 090 */ 091 protected boolean isRuleException(IToken operand) { 092 return false; 093 } 094 095 /** 096 * Returns list of start indices of comparison expressions in entity tokens. 097 */ 098 protected abstract List<Integer> getIndicesOfComparisonExpression(List<IToken> tokens); 099 100 /** 101 * Returns index of the second operand of comparison expression. 102 */ 103 protected abstract int getSecondOperandIndex(int operatorIndex); 104}