I now detect unused parameters
[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   /**
159    * Get global variables (not parameters)
160    * @return the variables from outside
161    */
162   public List getOutsideVariable() {
163     final ArrayList list = new ArrayList();
164
165     if (statements != null) {
166       for (int i = 0; i < statements.length; i++) {
167         list.addAll(statements[i].getOutsideVariable());
168       }
169     }
170     return list;
171   }
172
173   private List getParameters(final List list) {
174     if (arguments != null) {
175       final Enumeration vars = arguments.keys();
176       while (vars.hasMoreElements()) {
177         list.add(new VariableUsage((String) vars.nextElement(), sourceStart));
178       }
179     }
180     return list;
181   }
182
183   /**
184    * get the modified variables.
185    * @return the variables from we change value
186    */
187   public List getModifiedVariable() {
188     final ArrayList list = new ArrayList();
189     if (statements != null) {
190       for (int i = 0; i < statements.length; i++) {
191         list.addAll(statements[i].getModifiedVariable());
192       }
193     }
194     return list;
195   }
196
197   /**
198    * Get the variables used.
199    * @return the variables used
200    */
201   public List getUsedVariable() {
202     final ArrayList list = new ArrayList();
203     if (statements != null) {
204       for (int i = 0; i < statements.length; i++) {
205         list.addAll(statements[i].getUsedVariable());
206       }
207     }
208     return list;
209   }
210
211   private boolean isVariableDeclaredBefore(List list, VariableUsage var) {
212     final String name = var.getName();
213     final int pos = var.getStartOffset();
214     for (int i = 0; i < list.size(); i++) {
215       VariableUsage variableUsage = (VariableUsage) list.get(i);
216       if (variableUsage.getName().equals(name) && variableUsage.getStartOffset() < pos) {
217         return true;
218       }
219     }
220     return false;
221   }
222
223   private void dumpList(List list, String name) {
224     StringBuffer buff = new StringBuffer(name).append("\n");
225     for (int i = 0; i < list.size(); i++) {
226       buff.append(list.get(i).toString()).append("\n");
227     }
228     if (PHPeclipsePlugin.DEBUG) {
229       PHPeclipsePlugin.log(1, buff.toString());
230     }
231   }
232
233   /**
234    * This method will analyze the code.
235    */
236   public void analyzeCode() {
237     final List globalsVars = getOutsideVariable();
238     final List modifiedVars = getModifiedVariable();
239     final List parameters = getParameters(new ArrayList());
240
241     final List declaredVars = new ArrayList(globalsVars.size() + modifiedVars.size());
242     declaredVars.addAll(globalsVars);
243     declaredVars.addAll(modifiedVars);
244     declaredVars.addAll(parameters);
245
246     final List usedVars = getUsedVariable();
247     final List readOrWriteVars = new ArrayList(modifiedVars.size()+usedVars.size());
248     readOrWriteVars.addAll(modifiedVars);
249     readOrWriteVars.addAll(usedVars);
250 /*    dumpList(globalsVars, "outside");
251     dumpList(modifiedVars, "modified");
252     dumpList(usedVars, "used");  */
253
254
255     //look for used variables that were not declared before
256     findUnusedParameters(readOrWriteVars,parameters);
257     findUnknownUsedVars(usedVars, declaredVars);
258   }
259
260   /**
261    * This method will add a warning on all unused parameters.
262    * @param vars the used variable list
263    * @param parameters the declared variable list
264    */
265   private void findUnusedParameters(final List vars, final List parameters) {
266     for (int i = 0; i < parameters.size(); i++) {
267       VariableUsage param = ((VariableUsage)parameters.get(i));
268       if (!isVariableInList(param.getName(),vars)) {
269         try {
270           PHPParserSuperclass.setMarker("warning, the parameter "+param.getName() +" seems to be never used in your method",
271                                         param.getStartOffset(),
272                                         param.getStartOffset() + param.getName().length(),
273                                         PHPParserSuperclass.WARNING,
274                                         "");
275         } catch (CoreException e) {
276           PHPeclipsePlugin.log(e);
277         }
278       }
279     }
280   }
281
282   private boolean isVariableInList(final String name, final List list) {
283     for (int i = 0; i < list.size(); i++) {
284       if (((VariableUsage) list.get(i)).getName().equals(name)) {
285         return true;
286       }
287     }
288     return false;
289   }
290
291   /**
292    * This method will add a warning on all used variables in a method that aren't declared before.
293    * @param usedVars the used variable list
294    * @param declaredVars the declared variable list
295    */
296   private void findUnknownUsedVars(final List usedVars, final List declaredVars) {
297     for (int i = 0; i < usedVars.size(); i++) {
298       VariableUsage variableUsage = (VariableUsage) usedVars.get(i);
299       if (variableUsage.getName().equals("this")) continue; // this is a special variable
300       if (!isVariableDeclaredBefore(declaredVars, variableUsage)) {
301         try {
302           PHPParserSuperclass.setMarker("warning, usage of a variable that seems to be unassigned yet : " + variableUsage.getName(),
303                                         variableUsage.getStartOffset(),
304                                         variableUsage.getStartOffset() + variableUsage.getName().length(),
305                                         PHPParserSuperclass.WARNING,
306                                         "");
307         } catch (CoreException e) {
308           PHPeclipsePlugin.log(e);
309         }
310       }
311     }
312   }
313 }