Makefile fixup
[org.ibex.tool.git] / repo / org.ibex.tool / src / org / eclipse / jdt / internal / compiler / ast / Javadoc.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.ast;
12
13 import org.eclipse.jdt.core.compiler.CharOperation;
14 import org.eclipse.jdt.internal.compiler.lookup.*;
15
16 /**
17  * Node representing a structured Javadoc comment
18  */
19 public class Javadoc extends ASTNode {
20
21         public JavadocSingleNameReference[] parameters; // @param
22         public TypeReference[] thrownExceptions; // @throws, @exception
23         public JavadocReturnStatement returnStatement; // @return
24         public Expression[] references; // @see
25         public boolean inherited = false;
26         // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51600
27         // Store param references for tag with invalid syntax
28         public JavadocSingleNameReference[] invalidParameters; // @param
29
30         public Javadoc(int sourceStart, int sourceEnd) {
31                 this.sourceStart = sourceStart;
32                 this.sourceEnd = sourceEnd;
33         }
34         
35         /*
36          * @see org.eclipse.jdt.internal.compiler.ast.ASTNode#print(int, java.lang.StringBuffer)
37          */
38         public StringBuffer print(int indent, StringBuffer output) {
39                 printIndent(indent, output).append("/**\n"); //$NON-NLS-1$
40                 if (this.parameters != null) {
41                         for (int i = 0, length = this.parameters.length; i < length; i++) {
42                                 printIndent(indent + 1, output).append(" * @param "); //$NON-NLS-1$             
43                                 this.parameters[i].print(indent, output).append('\n');
44                         }
45                 }
46                 if (this.returnStatement != null) {
47                         printIndent(indent + 1, output).append(" * @return\n"); //$NON-NLS-1$           
48                 }
49                 if (this.thrownExceptions != null) {
50                         for (int i = 0, length = this.thrownExceptions.length; i < length; i++) {
51                                 printIndent(indent + 1, output).append(" * @throws "); //$NON-NLS-1$            
52                                 this.thrownExceptions[i].print(indent, output).append('\n');
53                         }
54                 }
55                 if (this.references != null) {
56                         for (int i = 0, length = this.references.length; i < length; i++) {
57                                 printIndent(indent + 1, output).append(" * @see"); //$NON-NLS-1$                
58                                 this.references[i].print(indent, output).append('\n');
59                         }
60                 }
61                 printIndent(indent, output).append(" */\n"); //$NON-NLS-1$
62                 return output;
63         }
64
65         /*
66          * Resolve type javadoc while a class scope
67          */
68         public void resolve(ClassScope classScope) {
69                 
70
71                 // @param tags
72                 int paramTagsSize = this.parameters == null ? 0 : this.parameters.length;
73                 for (int i = 0; i < paramTagsSize; i++) {
74                         JavadocSingleNameReference param = this.parameters[i];
75                         classScope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd);
76                 }
77
78                 // @return tags
79                 if (this.returnStatement != null) {
80                         classScope.problemReporter().javadocUnexpectedTag(this.returnStatement.sourceStart, this.returnStatement.sourceEnd);
81                 }
82
83                 // @throws/@exception tags
84                 int throwsTagsLength = this.thrownExceptions == null ? 0 : this.thrownExceptions.length;
85                 for (int i = 0; i < throwsTagsLength; i++) {
86                         TypeReference typeRef = this.thrownExceptions[i];
87                         int start, end;
88                         if (typeRef instanceof JavadocSingleTypeReference) {
89                                 JavadocSingleTypeReference singleRef = (JavadocSingleTypeReference) typeRef;
90                                 start = singleRef.tagSourceStart;
91                                 end = singleRef.tagSourceEnd;
92                         } else if (typeRef instanceof JavadocQualifiedTypeReference) {
93                                 JavadocQualifiedTypeReference qualifiedRef = (JavadocQualifiedTypeReference) typeRef;
94                                 start = qualifiedRef.tagSourceStart;
95                                 end = qualifiedRef.tagSourceEnd;
96                         } else {
97                                 start = typeRef.sourceStart;
98                                 end = typeRef.sourceEnd;
99                         }
100                         classScope.problemReporter().javadocUnexpectedTag(start, end);
101                 }
102
103                 // @see tags
104                 int seeTagsLength = this.references == null ? 0 : this.references.length;
105                 for (int i = 0; i < seeTagsLength; i++) {
106                         
107                         // Resolve reference
108                         this.references[i].resolveType(classScope);
109
110                         // Some unbound field reference might be changed to message send
111                         // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51911
112                         if (this.references[i] instanceof JavadocFieldReference) {
113                                 JavadocFieldReference fieldRef = (JavadocFieldReference) this.references[i];
114                                 if (fieldRef.receiverType != null && fieldRef.binding == null) { // binding was reset in case of valid method reference
115                                         // TODO (frederic) post 3.0 - avoid new instanciation of Compiler AST node
116                                         JavadocMessageSend msgSend = new JavadocMessageSend(fieldRef.token, fieldRef.nameSourcePosition);
117                                         msgSend.receiver = fieldRef.receiver;
118                                         msgSend.receiverType = fieldRef.receiverType;
119                                         msgSend.qualifyingType = fieldRef.receiverType;
120                                         msgSend.superAccess = classScope.enclosingSourceType().isCompatibleWith(msgSend.receiverType);
121                                         msgSend.binding = classScope.findMethod((ReferenceBinding)msgSend.receiverType, msgSend.selector, new TypeBinding[0], msgSend);
122                                         this.references[i] = msgSend;
123                                 }
124                         }
125                 }
126         }
127         
128         /*
129          * Resolve method javadoc while a method scope
130          */
131         public void resolve(MethodScope methScope) {
132                 
133                 // get method declaration
134                 AbstractMethodDeclaration methDecl = methScope.referenceMethod();
135                 boolean overriding = methDecl == null ? false : (methDecl.binding.modifiers & (AccImplementing+AccOverriding)) != 0;
136
137                 // @see tags
138                 int seeTagsLength = this.references == null ? 0 : this.references.length;
139                 boolean superRef = false;
140                 for (int i = 0; i < seeTagsLength; i++) {
141                         
142                         // Resolve reference
143                         this.references[i].resolveType(methScope);
144                         
145                         // Some unbound field reference might be changed to message send
146                         // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51911
147                         if (this.references[i] instanceof JavadocFieldReference) {
148                                 JavadocFieldReference fieldRef = (JavadocFieldReference) this.references[i];
149                                 if (fieldRef.receiverType != null && fieldRef.binding == null) { // binding was reset in case of valid method reference
150                                         // TODO (frederic) post 3.0 - avoid new instanciation of Compiler AST node
151                                         JavadocMessageSend msgSend = new JavadocMessageSend(fieldRef.token, fieldRef.nameSourcePosition);
152                                         msgSend.receiver = fieldRef.receiver;
153                                         msgSend.receiverType = fieldRef.receiverType;
154                                         msgSend.qualifyingType = fieldRef.receiverType;
155                                         msgSend.superAccess = methScope.enclosingSourceType().isCompatibleWith(msgSend.receiverType);
156                                         msgSend.binding = methScope.findMethod((ReferenceBinding)msgSend.receiverType, msgSend.selector, new TypeBinding[0], msgSend);
157                                         this.references[i] = msgSend;
158                                 }
159                         }
160
161                         // see whether we can have a super reference
162                         try {
163                                 if (methDecl != null && (methDecl.isConstructor() || overriding) && !superRef) {
164                                         if (this.references[i] instanceof JavadocMessageSend) {
165                                                 JavadocMessageSend messageSend = (JavadocMessageSend) this.references[i];
166                                                 // if binding is valid then look if we have a reference to an overriden method/constructor
167                                                 if (messageSend.binding != null && messageSend.binding.isValidBinding()) {
168                                                         if (methDecl.binding.declaringClass.isCompatibleWith(messageSend.receiverType) &&
169                                                                 CharOperation.equals(messageSend.selector, methDecl.selector) &&
170                                                                 (messageSend.binding.returnType == methDecl.binding.returnType)) {
171                                                                 if (messageSend.arguments == null && methDecl.arguments == null) {
172                                                                         superRef = true;
173                                                                 }
174                                                                 else if (messageSend.arguments != null && methDecl.arguments != null) {
175                                                                         superRef = methDecl.binding.areParametersEqual(messageSend.binding);
176                                                                 }
177                                                         }
178                                                 }
179                                         }
180                                         else if (this.references[i] instanceof JavadocAllocationExpression) {
181                                                 JavadocAllocationExpression allocationExpr = (JavadocAllocationExpression) this.references[i];
182                                                 // if binding is valid then look if we have a reference to an overriden method/constructor
183                                                 if (allocationExpr.binding != null && allocationExpr.binding.isValidBinding()) {
184                                                         if (methDecl.binding.declaringClass.isCompatibleWith(allocationExpr.resolvedType)) {
185                                                                 if (allocationExpr.arguments == null && methDecl.arguments == null) {
186                                                                         superRef = true;
187                                                                 }
188                                                                 else if (allocationExpr.arguments != null && methDecl.arguments != null) {
189                                                                         superRef = methDecl.binding.areParametersEqual(allocationExpr.binding);
190                                                                 }
191                                                         }
192                                                 }
193                                         }
194                                 }
195                         }
196                         catch (Exception e) {
197                                 // Something wrong happen, forget super ref...
198                         }
199                 }
200                 
201                 // Store if a reference exists to an overriden method/constructor or the method is in a local type,
202                 boolean reportMissing = methDecl == null || !((overriding && this.inherited) || superRef || (methDecl.binding.declaringClass != null && methDecl.binding.declaringClass.isLocalType()));
203
204                 // @param tags
205                 resolveParamTags(methScope, reportMissing);
206
207                 // @return tags
208                 if (this.returnStatement == null) {
209                         if (reportMissing && methDecl != null) {
210                                 if (!methDecl.isConstructor() && !methDecl.isClinit()) {
211                                         MethodDeclaration meth = (MethodDeclaration) methDecl;
212                                         if (meth.binding.returnType != VoidBinding) {
213                                                 // method with return should have @return tag
214                                                 methScope.problemReporter().javadocMissingReturnTag(meth.returnType.sourceStart, meth.returnType.sourceEnd, methDecl.binding.modifiers);
215                                         }
216                                 }
217                         }
218                 } else {
219                         this.returnStatement.resolve(methScope);
220                 }
221
222                 // @throws/@exception tags
223                 resolveThrowsTags(methScope, reportMissing);
224
225                 // Resolve param tags with invalid syntax
226                 int length = this.invalidParameters == null ? 0 : this.invalidParameters.length;
227                 for (int i = 0; i < length; i++) {
228                         this.invalidParameters[i].resolve(methScope, false);
229                 }
230         }
231         
232         /*
233          * Resolve @param tags while method scope
234          */
235         private void resolveParamTags(MethodScope methScope, boolean reportMissing) {
236                 AbstractMethodDeclaration md = methScope.referenceMethod();
237                 int paramTagsSize = this.parameters == null ? 0 : this.parameters.length;
238
239                 // If no referenced method (field initializer for example) then report a problem for each param tag
240                 if (md == null) {
241                         for (int i = 0; i < paramTagsSize; i++) {
242                                 JavadocSingleNameReference param = this.parameters[i];
243                                 methScope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd);
244                         }
245                         return;
246                 }
247                 
248                 // If no param tags then report a problem for each method argument
249                 int argumentsSize = md.arguments == null ? 0 : md.arguments.length;
250                 if (paramTagsSize == 0) {
251                         if (reportMissing) {
252                                 for (int i = 0; i < argumentsSize; i++) {
253                                         Argument arg = md.arguments[i];
254                                         methScope.problemReporter().javadocMissingParamTag(arg, md.binding.modifiers);
255                                 }
256                         }
257                 } else {
258                         LocalVariableBinding[] bindings = new LocalVariableBinding[paramTagsSize];
259                         int maxBindings = 0;
260
261                         // Scan all @param tags
262                         for (int i = 0; i < paramTagsSize; i++) {
263                                 JavadocSingleNameReference param = this.parameters[i];
264                                 param.resolve(methScope);
265                                 if (param.binding != null && param.binding.isValidBinding()) {
266                                         // Verify duplicated tags
267                                         boolean found = false;
268                                         for (int j = 0; j < maxBindings && !found; j++) {
269                                                 if (bindings[j] == param.binding) {
270                                                         methScope.problemReporter().javadocDuplicatedParamTag(param, md.binding.modifiers);
271                                                         found = true;
272                                                 }
273                                         }
274                                         if (!found) {
275                                                 bindings[maxBindings++] = (LocalVariableBinding) param.binding;
276                                         }
277                                 }
278                         }
279
280                         // Look for undocumented arguments
281                         if (reportMissing) {
282                                 for (int i = 0; i < argumentsSize; i++) {
283                                         Argument arg = md.arguments[i];
284                                         boolean found = false;
285                                         for (int j = 0; j < maxBindings && !found; j++) {
286                                                 LocalVariableBinding binding = bindings[j];
287                                                 if (arg.binding == binding) {
288                                                         found = true;
289                                                 }
290                                         }
291                                         if (!found) {
292                                                 methScope.problemReporter().javadocMissingParamTag(arg, md.binding.modifiers);
293                                         }
294                                 }
295                         }
296                 }
297         }
298
299         /*
300          * Resolve @throws/@exception tags while method scope
301          */
302         private void resolveThrowsTags(MethodScope methScope, boolean reportMissing) {
303                 AbstractMethodDeclaration md = methScope.referenceMethod();
304                 int throwsTagsLength = this.thrownExceptions == null ? 0 : this.thrownExceptions.length;
305
306                 // If no referenced method (field initializer for example) then report a problem for each throws tag
307                 if (md == null) {
308                         for (int i = 0; i < throwsTagsLength; i++) {
309                                 TypeReference typeRef = this.thrownExceptions[i];
310                                 int start = typeRef.sourceStart;
311                                 int end = typeRef.sourceEnd;
312                                 if (typeRef instanceof JavadocQualifiedTypeReference) {
313                                         start = ((JavadocQualifiedTypeReference) typeRef).tagSourceStart;
314                                         end = ((JavadocQualifiedTypeReference) typeRef).tagSourceEnd;
315                                 } else if (typeRef instanceof JavadocSingleTypeReference) {
316                                         start = ((JavadocSingleTypeReference) typeRef).tagSourceStart;
317                                         end = ((JavadocSingleTypeReference) typeRef).tagSourceEnd;
318                                 }
319                                 methScope.problemReporter().javadocUnexpectedTag(start, end);
320                         }
321                         return;
322                 }
323
324                 // If no throws tags then report a problem for each method thrown exception
325                 int boundExceptionLength = (md.binding == null || md.binding.thrownExceptions == null) ? 0 : md.binding.thrownExceptions.length;
326                 int thrownExceptionLength = md.thrownExceptions == null ? 0 : md.thrownExceptions.length;
327                 if (throwsTagsLength == 0) {
328                         if (reportMissing) {
329                                 for (int i = 0; i < boundExceptionLength; i++) {
330                                         ReferenceBinding exceptionBinding = md.binding.thrownExceptions[i];
331                                         if (exceptionBinding != null && exceptionBinding.isValidBinding()) { // flag only valid class name
332                                                 int j=i;
333                                                 while (j<thrownExceptionLength && exceptionBinding != md.thrownExceptions[j].resolvedType) j++;
334                                                 if (j<thrownExceptionLength) {
335                                                         methScope.problemReporter().javadocMissingThrowsTag(md.thrownExceptions[j], md.binding.modifiers);
336                                                 }
337                                         }
338                                 }
339                         }
340                 } else {
341                         int maxRef = 0;
342                         TypeReference[] typeReferences = new TypeReference[throwsTagsLength];
343
344                         // Scan all @throws tags
345                         for (int i = 0; i < throwsTagsLength; i++) {
346                                 TypeReference typeRef = this.thrownExceptions[i];
347                                 typeRef.resolve(methScope);
348                                 TypeBinding typeBinding = typeRef.resolvedType;
349
350                                 if (typeBinding != null && typeBinding.isValidBinding() && typeBinding.isClass()) {
351                                         typeReferences[maxRef++] = typeRef;
352                                 }
353                         }
354
355                         // Look for undocumented thrown exception
356                         for (int i = 0; i < boundExceptionLength; i++) {
357                                 ReferenceBinding exceptionBinding = md.binding.thrownExceptions[i];
358                                 boolean found = false;
359                                 for (int j = 0; j < maxRef && !found; j++) {
360                                         if (typeReferences[j] != null) {
361                                                 TypeBinding typeBinding = typeReferences[j].resolvedType;
362                                                 if (exceptionBinding == typeBinding) {
363                                                         found = true;
364                                                         typeReferences[j] = null;
365                                                 }
366                                         }
367                                 }
368                                 if (!found && reportMissing) {
369                                         if (exceptionBinding != null && exceptionBinding.isValidBinding()) { // flag only valid class name
370                                                 int k=i;
371                                                 while (k<thrownExceptionLength && exceptionBinding != md.thrownExceptions[k].resolvedType) k++;
372                                                 if (k<thrownExceptionLength) {
373                                                         methScope.problemReporter().javadocMissingThrowsTag(md.thrownExceptions[k], md.binding.modifiers);
374                                                 }
375                                         }
376                                 }
377                         }
378
379                         // Verify additional @throws tags
380                         for (int i = 0; i < maxRef; i++) {
381                                 TypeReference typeRef = typeReferences[i];
382                                 if (typeRef != null) {
383                                         boolean compatible = false;
384                                         // thrown exceptions subclasses are accepted
385                                         for (int j = 0; j<thrownExceptionLength && !compatible; j++) {
386                                                 TypeBinding exceptionBinding = md.thrownExceptions[j].resolvedType;
387                                                 if (exceptionBinding != null) {
388                                                         compatible = typeRef.resolvedType.isCompatibleWith(exceptionBinding);
389                                                 }
390                                         }
391                         
392                                         //  If not compatible only complain on unchecked exception
393                                         if (!compatible &&
394                                                  !typeRef.resolvedType.isCompatibleWith(methScope.getJavaLangRuntimeException()) &&
395                                                  !typeRef.resolvedType.isCompatibleWith(methScope.getJavaLangError())) {
396                                                 methScope.problemReporter().javadocInvalidThrowsClassName(typeRef, md.binding.modifiers);
397                                         }
398                                 }
399                         }
400                 }
401         }
402         
403         /*
404          * Search node with a given staring position in javadoc objects arrays.
405          */
406         public ASTNode getNodeStartingAt(int start) {
407                 // parameters array
408                 if (this.parameters != null) {
409                         for (int i=0; i<this.parameters.length; i++) {
410                                 JavadocSingleNameReference param = this.parameters[i];
411                                 if (param.sourceStart==start) {
412                                         return param;
413                                 }
414                         }
415                 }
416                 // array of invalid syntax tags parameters
417                 if (this.invalidParameters != null) {
418                         for (int i=0; i<this.invalidParameters.length; i++) {
419                                 JavadocSingleNameReference param = this.invalidParameters[i];
420                                 if (param.sourceStart==start) {
421                                         return param;
422                                 }
423                         }
424                 }
425                 // thrown exception array
426                 if (this.thrownExceptions != null) {
427                         for (int i=0; i<this.thrownExceptions.length; i++) {
428                                 TypeReference typeRef = this.thrownExceptions[i];
429                                 if (typeRef.sourceStart==start) {
430                                         return typeRef;
431                                 }
432                         }
433                 }
434                 // references array
435                 if (this.references != null) {
436                         for (int i=0; i<this.references.length; i++) {
437                                 org.eclipse.jdt.internal.compiler.ast.Expression expression = this.references[i];
438                                 if (expression.sourceStart==start) {
439                                         return expression;
440                                 } else if (expression instanceof JavadocAllocationExpression) {
441                                         JavadocAllocationExpression allocationExpr = (JavadocAllocationExpression) this.references[i];
442                                         // if binding is valid then look at arguments
443                                         if (allocationExpr.binding != null && allocationExpr.binding.isValidBinding()) {
444                                                 if (allocationExpr.arguments != null) {
445                                                         for (int j=0; j<allocationExpr.arguments.length; j++) {
446                                                                 if (allocationExpr.arguments[j].sourceStart == start) {
447                                                                         return allocationExpr.arguments[j];
448                                                                 }
449                                                         }
450                                                 }
451                                         }
452                                 } else if (expression instanceof JavadocMessageSend) {
453                                         JavadocMessageSend messageSend = (JavadocMessageSend) this.references[i];
454                                         // if binding is valid then look at arguments
455                                         if (messageSend.binding != null && messageSend.binding.isValidBinding()) {
456                                                 if (messageSend.arguments != null) {
457                                                         for (int j=0; j<messageSend.arguments.length; j++) {
458                                                                 if (messageSend.arguments[j].sourceStart == start) {
459                                                                         return messageSend.arguments[j];
460                                                                 }
461                                                         }
462                                                 }
463                                         }
464                                 }
465                         }
466                 }
467                 return null;
468         }
469 }