a bugfix on variables
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / MethodDeclaration.java
1 package net.sourceforge.phpdt.internal.compiler.ast;
2
3 import net.sourceforge.phpdt.internal.compiler.parser.OutlineableWithChildren;
4 import net.sourceforge.phpdt.internal.compiler.parser.Outlineable;
5 import net.sourceforge.phpdt.internal.compiler.ast.declarations.VariableUsage;
6 import net.sourceforge.phpdt.internal.ui.PHPUiImages;
7 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
8 import org.eclipse.jface.resource.ImageDescriptor;
9 import org.eclipse.jface.text.Position;
10 import org.eclipse.core.runtime.CoreException;
11
12 import java.util.Hashtable;
13 import java.util.Enumeration;
14 import java.util.ArrayList;
15 import java.util.List;
16
17 import test.PHPParserSuperclass;
18
19 /**
20  * A Method declaration.
21  * @author Matthieu Casanova
22  */
23 public class MethodDeclaration extends Statement implements OutlineableWithChildren {
24
25   /** The name of the method. */
26   public String name;
27   public Hashtable arguments;
28
29
30   public Statement[] statements;
31   public int bodyStart;
32   public int bodyEnd = -1;
33   /** Tell if the method is a class constructor. */
34   public boolean isConstructor;
35
36   /** The parent object. */
37   private Object parent;
38   /** The outlineable children (those will be in the node array too. */
39   private ArrayList children = new ArrayList();
40
41   /** Tell if the method returns a reference. */
42   public boolean reference;
43
44   private Position position;
45
46   public MethodDeclaration(final Object parent,
47                            final String name,
48                            final Hashtable arguments,
49                            final boolean reference,
50                            final int sourceStart,
51                            final int sourceEnd,
52                            final int bodyStart,
53                            final int bodyEnd) {
54     super(sourceStart, sourceEnd);
55     this.name = name;
56     this.arguments = arguments;
57     this.parent = parent;
58     this.reference = reference;
59     this.bodyStart = bodyStart;
60     this.bodyEnd = bodyEnd;
61     position = new Position(sourceStart, sourceEnd);
62   }
63
64   /**
65    * Return method into String, with a number of tabs
66    * @param tab the number of tabs
67    * @return the String containing the method
68    */
69   public String toString(final int tab) {
70     final StringBuffer buff = new StringBuffer(tabString(tab));
71     buff.append(toStringHeader());
72     buff.append(toStringStatements(tab + 1));
73     return buff.toString();
74   }
75
76   public String toStringHeader() {
77     return "function " + toString();
78   }
79
80   /**
81    * Return the statements of the method into Strings
82    * @param tab the number of tabs
83    * @return the String containing the statements
84    */
85   public String toStringStatements(final int tab) {
86     final StringBuffer buff = new StringBuffer(" {"); //$NON-NLS-1$
87     if (statements != null) {
88       for (int i = 0; i < statements.length; i++) {
89         buff.append("\n").append(statements[i].toString(tab)); //$NON-NLS-1$
90         if (!(statements[i] instanceof Block)) {
91           buff.append(";"); //$NON-NLS-1$
92         }
93       }
94     }
95     buff.append("\n").append(tabString(tab == 0 ? 0 : tab - 1)).append("}"); //$NON-NLS-2$ //$NON-NLS-1$
96     return buff.toString();
97   }
98
99   /**
100    * Get the image of a class.
101    * @return the image that represents a php class
102    */
103   public ImageDescriptor getImage() {
104     return PHPUiImages.DESC_FUN;
105   }
106
107   public void setParent(final Object parent) {
108     this.parent = parent;
109   }
110
111   public Object getParent() {
112     return parent;
113   }
114
115   public boolean add(final Outlineable o) {
116     return children.add(o);
117   }
118
119   public Outlineable get(final int index) {
120     return (Outlineable) children.get(index);
121   }
122
123   public int size() {
124     return children.size();
125   }
126
127   public String toString() {
128     final StringBuffer buff = new StringBuffer();
129     if (reference) {
130       buff.append("&");//$NON-NLS-1$
131     }
132     buff.append(name).append("(");//$NON-NLS-1$
133
134     if (arguments != null) {
135       final Enumeration values = arguments.elements();
136       int i = 0;
137       while (values.hasMoreElements()) {
138         final VariableDeclaration o = (VariableDeclaration) values.nextElement();
139         buff.append(o.toStringExpression());
140         if (i != (arguments.size() - 1)) {
141           buff.append(", "); //$NON-NLS-1$
142         }
143         i++;
144       }
145     }
146     buff.append(")"); //$NON-NLS-1$
147     return buff.toString();
148   }
149
150   public Position getPosition() {
151     return position;
152   }
153
154   public List getList() {
155     return children;
156   }
157
158   /** no outside variables. */
159   public List getOutsideVariable() {
160     return new ArrayList(1);
161   }
162
163   public List getModifiedVariable() {
164     return new ArrayList(1);
165   }
166
167   public List getUsedVariable() {
168     return new ArrayList(1);
169   }
170
171   /**
172    * Get global variables (not parameters).
173    * @return the variables from outside
174    */
175   public List getGlobalVariable() {
176     final ArrayList list = new ArrayList();
177
178     if (statements != null) {
179       for (int i = 0; i < statements.length; i++) {
180         list.addAll(statements[i].getOutsideVariable());
181       }
182     }
183     return list;
184   }
185
186   private List getParameters(final List list) {
187     if (arguments != null) {
188       final Enumeration vars = arguments.elements();
189       while (vars.hasMoreElements()) {
190         final VariableDeclaration variable = (VariableDeclaration) vars.nextElement();
191         list.add(new VariableUsage(variable.name(), variable.sourceStart));
192       }
193     }
194     return list;
195   }
196
197   /**
198    * get the modified variables.
199    * @return the variables from we change value
200    */
201   private List getAssignedVariableInCode() {
202     final ArrayList list = new ArrayList();
203     if (statements != null) {
204       for (int i = 0; i < statements.length; i++) {
205         list.addAll(statements[i].getModifiedVariable());
206       }
207     }
208     return list;
209   }
210
211   /**
212    * Get the variables used.
213    * @return the variables used
214    */
215   private List getUsedVariableInCode() {
216     final ArrayList list = new ArrayList();
217     if (statements != null) {
218       for (int i = 0; i < statements.length; i++) {
219         list.addAll(statements[i].getUsedVariable());
220       }
221     }
222     return list;
223   }
224
225   private boolean isVariableDeclaredBefore(final List list, final VariableUsage var) {
226     final String name = var.getName();
227     final int pos = var.getStartOffset();
228     for (int i = 0; i < list.size(); i++) {
229       final VariableUsage variableUsage = (VariableUsage) list.get(i);
230       if (variableUsage.getName().equals(name) && variableUsage.getStartOffset() < pos) {
231         return true;
232       }
233     }
234     return false;
235   }
236
237   /** This method will analyze the code. */
238   public void analyzeCode() {
239     if (statements != null) {
240       for (int i = 0; i < statements.length; i++) {
241         statements[i].analyzeCode();
242
243       }
244     }
245
246     final List globalsVars = getGlobalVariable();
247     final List modifiedVars = getAssignedVariableInCode();
248     final List parameters = getParameters(new ArrayList());
249
250     final List declaredVars = new ArrayList(globalsVars.size() + modifiedVars.size());
251     declaredVars.addAll(globalsVars);
252     declaredVars.addAll(modifiedVars);
253     declaredVars.addAll(parameters);
254
255     final List usedVars = getUsedVariableInCode();
256     final List readOrWriteVars = new ArrayList(modifiedVars.size()+usedVars.size());
257     readOrWriteVars.addAll(modifiedVars);
258     readOrWriteVars.addAll(usedVars);
259
260     //look for used variables that were not declared before
261     findUnusedParameters(readOrWriteVars,parameters);
262     findUnknownUsedVars(usedVars, declaredVars);
263   }
264
265   /**
266    * This method will add a warning on all unused parameters.
267    * @param vars the used variable list
268    * @param parameters the declared variable list
269    */
270   private void findUnusedParameters(final List vars, final List parameters) {
271     for (int i = 0; i < parameters.size(); i++) {
272       final VariableUsage param = ((VariableUsage)parameters.get(i));
273       if (!isVariableInList(param.getName(),vars)) {
274         try {
275           PHPParserSuperclass.setMarker("warning, the parameter "+param.getName() +" seems to be never used in your method",
276                                         param.getStartOffset(),
277                                         param.getStartOffset() + param.getName().length(),
278                                         PHPParserSuperclass.WARNING,
279                                         "");
280         } catch (CoreException e) {
281           PHPeclipsePlugin.log(e);
282         }
283       }
284     }
285   }
286
287   private boolean isVariableInList(final String name, final List list) {
288     for (int i = 0; i < list.size(); i++) {
289       if (((VariableUsage) list.get(i)).getName().equals(name)) {
290         return true;
291       }
292     }
293     return false;
294   }
295
296   /**
297    * This method will add a warning on all used variables in a method that aren't declared before.
298    * @param usedVars the used variable list
299    * @param declaredVars the declared variable list
300    */
301   private void findUnknownUsedVars(final List usedVars, final List declaredVars) {
302     for (int i = 0; i < usedVars.size(); i++) {
303       final VariableUsage variableUsage = (VariableUsage) usedVars.get(i);
304       if (variableUsage.getName().equals("this")) continue; // this is a special variable
305       if (!isVariableDeclaredBefore(declaredVars, variableUsage)) {
306         try {
307           PHPParserSuperclass.setMarker("warning, usage of a variable that seems to be unassigned yet : " + variableUsage.getName(),
308                                         variableUsage.getStartOffset(),
309                                         variableUsage.getStartOffset() + variableUsage.getName().length(),
310                                         PHPParserSuperclass.WARNING,
311                                         "");
312         } catch (CoreException e) {
313           PHPeclipsePlugin.log(e);
314         }
315       }
316     }
317   }
318 }