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