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