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