summaryrefslogtreecommitdiffstats
path: root/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/checkers/sample/SuggestedParenthesisChecker.java
blob: 27712b460bb32ac123505de9a0b7e17bd9ea483b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*******************************************************************************
 * Copyright (c) 2009 Alena Laskavaia 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Alena Laskavaia  - initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.codan.checkers.sample;

import org.eclipse.cdt.codan.core.model.AbstractIndexAstChecker;
import org.eclipse.cdt.core.dom.ast.ASTNodeProperty;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTForStatement;
import org.eclipse.cdt.core.dom.ast.IASTIfStatement;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;

/**
 * This checker finds a problems that cause by lack of understanding operator
 * precedence in C. In any case it is better to surround expressions in
 * parenthesis to improve readability. Example: ! x>0 && x<10 (this would be
 * (!x)>0 && x<10 in C) We only look for &&, || and ! operators (and binary | &
 * ^ ~)
 * 
 * @author Alena
 * 
 */
public class SuggestedParenthesisChecker extends AbstractIndexAstChecker {
	private static final String ER_ID = "org.eclipse.cdt.codan.checkers.sample.SuggestedParenthesisProblem";

	public void processAst(IASTTranslationUnit ast) {
		// traverse the ast using the visitor pattern.
		ast.accept(new ExpressionVisitor());
	}

	class ExpressionVisitor extends ASTVisitor {
		private SuspiciousExpressionVisitor svis;

		ExpressionVisitor() {
			shouldVisitExpressions = true;
			svis = new SuspiciousExpressionVisitor();
		}

		public int visit(IASTExpression expression) {
			int precedence = getPrecedence(expression);
			if (precedence == 2) { // unary not
				if (isUsedAsOperand(expression)) {
					reportProblem(ER_ID, expression,
							"Suggested parenthesis around expression");
					return PROCESS_SKIP;
				}
			}
			if (precedence >= 0) {
				synchronized (svis) { // since we use only one instance of this
										// visitor sync just in case
					svis.init(expression);
					expression.accept(svis);
					if (svis.suspicious == true) {
						reportProblem(ER_ID, svis.other,
								"Suggested parenthesis around expression");
						return PROCESS_SKIP;
					}
				}
			}
			return PROCESS_CONTINUE;
		}

		private boolean isUsedAsOperand(IASTExpression expression) {
			ASTNodeProperty prop = expression.getPropertyInParent();
			if (prop == IASTBinaryExpression.OPERAND_ONE
					|| prop == IASTBinaryExpression.OPERAND_TWO
					|| prop == IASTUnaryExpression.OPERAND)
				return true;
			return false;
		}
	}

	private int getPrecedence(IASTExpression e) {
		if (e instanceof IASTBinaryExpression) {
			IASTBinaryExpression binExpr = (IASTBinaryExpression) e;
			int operator = binExpr.getOperator();
			if (operator == IASTBinaryExpression.op_binaryAnd)
				return 8;
			if (operator == IASTBinaryExpression.op_binaryXor)
				return 9;
			if (operator == IASTBinaryExpression.op_binaryOr)
				return 10;
			if (operator == IASTBinaryExpression.op_logicalAnd)
				return 11;
			if (operator == IASTBinaryExpression.op_logicalOr)
				return 12;
		}
		if (e instanceof IASTUnaryExpression) {
			IASTUnaryExpression binExpr = (IASTUnaryExpression) e;
			int operator = binExpr.getOperator();
			if (operator == IASTUnaryExpression.op_not)
				return 2;
			if (operator == IASTUnaryExpression.op_tilde)
				return 2;
		}
		return -1;
	}

	class SuspiciousExpressionVisitor extends ASTVisitor {
		IASTExpression parent;
		IASTExpression other;
		boolean suspicious = false;

		void init(IASTExpression e) {
			parent = e;
			suspicious = false;
		}

		SuspiciousExpressionVisitor() {
			shouldVisitExpressions = true;
		}

		public int visit(IASTExpression expression) {
			if (expression == parent)
				return PROCESS_CONTINUE;
			if (expression instanceof IASTUnaryExpression) {
				IASTUnaryExpression uExpr = (IASTUnaryExpression) expression;
				int operator = uExpr.getOperator();
				if (operator == IASTUnaryExpression.op_bracketedPrimary) {
					return PROCESS_SKIP;
				}
			}
			if (getPrecedence(expression) < 0) // not considered operator
				return PROCESS_CONTINUE;
			if (getPrecedence(expression) == getPrecedence(parent)) {
				return PROCESS_SKIP;
			}
			suspicious = true;
			other = expression;
			return PROCESS_ABORT;
		}
	}
}