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