summaryrefslogtreecommitdiffstats
path: root/org.eclipse.cdt.codan.extension/src/org/eclipse/cdt/codan/extension/checkers/CloseOpenedFilesChecker.java
blob: d60889a5c60da7ca735fe10bb55fe088dbdcd904 (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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*******************************************************************************
 * Copyright (c) 2009 Elliott Baron
 * 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:
 *    Elliott Baron - initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.codan.extension.checkers;

import java.io.File;
import java.net.URI;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Set;

import org.eclipse.cdt.codan.core.model.AbstractIndexAstChecker;
import org.eclipse.cdt.codan.extension.Activator;
import org.eclipse.cdt.codan.extension.ExecutionState;
import org.eclipse.cdt.codan.extension.IPropertyFSM;
import org.eclipse.cdt.codan.extension.PropertySimulator;
import org.eclipse.cdt.codan.extension.PropertyState;
import org.eclipse.cdt.codan.extension.SymbolicState;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.ptp.pldt.mpi.analysis.cdt.graphs.GraphCreator;
import org.eclipse.ptp.pldt.mpi.analysis.cdt.graphs.ICallGraph;
import org.eclipse.ptp.pldt.mpi.analysis.cdt.graphs.ICallGraphNode;
import org.eclipse.ptp.pldt.mpi.analysis.cdt.graphs.IControlFlowGraph;
import org.eclipse.ptp.pldt.mpi.analysis.cdt.graphs.impl.ControlFlowGraph;

public class CloseOpenedFilesChecker extends AbstractIndexAstChecker {
	private static final String ERR_ID = Activator.PLUGIN_ID + ".CloseOpenedFilesProblem";
	
	private static final String OPEN = "open";
	private static final String CLOSE = "close";
	
	private IPropertyFSM fsm;
	
	// Property FSM states
	private PropertyState uninit;
	private PropertyState error;
	private PropertyState opened;

	public CloseOpenedFilesChecker() {
		fsm = initFSM();
	}

	public void processAst(IASTTranslationUnit ast) {
		GraphCreator creator = new GraphCreator();

		// Retrieve resource corresponding to this translation unit
		String path = ast.getFilePath();
		URI fileURI = new File(path).toURI();
		IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
		IResource[] resources = wsRoot.findFilesForLocationURI(fileURI);
		if (resources != null && resources.length > 0) {
			IProject proj = resources[0].getProject();

			// Create call graph for project
			ICallGraph cg = creator.createCallGraph(proj);
			creator.computeCallGraph(cg);

			// Create control flow graph for each function
			for (ICallGraphNode node : cg.getAllNodes()) {
				IASTStatement fnBody = node.getFuncDef().getBody();
				IControlFlowGraph cfg = new ControlFlowGraph(fnBody);
				cfg.buildCFG();

				// Search for error states using property simulation algorithm
				PropertySimulator sim = new PropertySimulator(cfg, fsm);

				// Check if the exit edge of the CFG contains an error state
				for (SymbolicState s : sim.getEndStates()) {
					if (s.getPropertyStates().contains(error)) {
						// Report problems
						for (IASTNode errorNode : s.getErrorCauses()) {
							reportProblem(errorNode, s.getExecutionState());
						}
					}
				}
			}
		}		
	}
	
	private IPropertyFSM initFSM() {
		uninit = new PropertyState("$u") {			
			@Override
			public PropertyState transition(IASTNode node) {
				PropertyState dest = uninit;
				if (containsOpen(node)) {
					dest = opened;
				}
				else if (containsClose(node)) {
					dest = error;
				}
				return dest;
			}
		};
		
		opened = new PropertyState("o") {			
			@Override
			public PropertyState transition(IASTNode node) {
				PropertyState dest = opened;
				if (containsOpen(node)) {
					dest = error;
				}
				if (containsClose(node)) {
					dest = uninit;
				}
				return dest;
			}
		};
		
		error = new PropertyState("$e") {
			
			@Override
			public PropertyState transition(IASTNode node) {
				return error;
			}
		};
		
		return new IPropertyFSM() {
			
			@Override
			public PropertyState getUninitState() {
				return uninit;
			}
			
			@Override
			public Set<PropertyState> getPropertyStates() {
				Set<PropertyState> states = new HashSet<PropertyState>();
				states.add(uninit);
				states.add(opened);
				states.add(error);
				return states;
			}
			
			@Override
			public PropertyState getErrorState() {
				return error;
			}
		};
	}

	protected boolean containsOpen(IASTNode node) {
		FunctionNameParser parser = new FunctionNameParser(node, OPEN, new String[] { "const char *", "int" });
		return parser.matches();
	}

	protected boolean containsClose(IASTNode node) {
		FunctionNameParser parser = new FunctionNameParser(node, CLOSE, new String[] { "int" });
		return parser.matches();
	}

	private void reportProblem(IASTNode node, ExecutionState condition) {
		String message;
		if (condition.isTop()) {
			message = "Improper use of open/close.";	
		}
		else {
			message = MessageFormat.format("Improper use of open/close given {0}.", condition);			
		}
		reportProblem(ERR_ID, node, message);
	}

}