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