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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.ast;
13 import org.eclipse.jdt.core.compiler.CharOperation;
14 import org.eclipse.jdt.internal.compiler.lookup.*;
17 * Node representing a structured Javadoc comment
19 public class Javadoc extends ASTNode {
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
30 public Javadoc(int sourceStart, int sourceEnd) {
31 this.sourceStart = sourceStart;
32 this.sourceEnd = sourceEnd;
36 * @see org.eclipse.jdt.internal.compiler.ast.ASTNode#print(int, java.lang.StringBuffer)
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');
46 if (this.returnStatement != null) {
47 printIndent(indent + 1, output).append(" * @return\n"); //$NON-NLS-1$
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');
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');
61 printIndent(indent, output).append(" */\n"); //$NON-NLS-1$
66 * Resolve type javadoc while a class scope
68 public void resolve(ClassScope classScope) {
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);
79 if (this.returnStatement != null) {
80 classScope.problemReporter().javadocUnexpectedTag(this.returnStatement.sourceStart, this.returnStatement.sourceEnd);
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];
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;
97 start = typeRef.sourceStart;
98 end = typeRef.sourceEnd;
100 classScope.problemReporter().javadocUnexpectedTag(start, end);
104 int seeTagsLength = this.references == null ? 0 : this.references.length;
105 for (int i = 0; i < seeTagsLength; i++) {
108 this.references[i].resolveType(classScope);
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;
129 * Resolve method javadoc while a method scope
131 public void resolve(MethodScope methScope) {
133 // get method declaration
134 AbstractMethodDeclaration methDecl = methScope.referenceMethod();
135 boolean overriding = methDecl == null ? false : (methDecl.binding.modifiers & (AccImplementing+AccOverriding)) != 0;
138 int seeTagsLength = this.references == null ? 0 : this.references.length;
139 boolean superRef = false;
140 for (int i = 0; i < seeTagsLength; i++) {
143 this.references[i].resolveType(methScope);
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;
161 // see whether we can have a super reference
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) {
174 else if (messageSend.arguments != null && methDecl.arguments != null) {
175 superRef = methDecl.binding.areParametersEqual(messageSend.binding);
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) {
188 else if (allocationExpr.arguments != null && methDecl.arguments != null) {
189 superRef = methDecl.binding.areParametersEqual(allocationExpr.binding);
196 catch (Exception e) {
197 // Something wrong happen, forget super ref...
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()));
205 resolveParamTags(methScope, reportMissing);
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);
219 this.returnStatement.resolve(methScope);
222 // @throws/@exception tags
223 resolveThrowsTags(methScope, reportMissing);
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);
233 * Resolve @param tags while method scope
235 private void resolveParamTags(MethodScope methScope, boolean reportMissing) {
236 AbstractMethodDeclaration md = methScope.referenceMethod();
237 int paramTagsSize = this.parameters == null ? 0 : this.parameters.length;
239 // If no referenced method (field initializer for example) then report a problem for each param tag
241 for (int i = 0; i < paramTagsSize; i++) {
242 JavadocSingleNameReference param = this.parameters[i];
243 methScope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd);
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) {
252 for (int i = 0; i < argumentsSize; i++) {
253 Argument arg = md.arguments[i];
254 methScope.problemReporter().javadocMissingParamTag(arg, md.binding.modifiers);
258 LocalVariableBinding[] bindings = new LocalVariableBinding[paramTagsSize];
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);
275 bindings[maxBindings++] = (LocalVariableBinding) param.binding;
280 // Look for undocumented arguments
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) {
292 methScope.problemReporter().javadocMissingParamTag(arg, md.binding.modifiers);
300 * Resolve @throws/@exception tags while method scope
302 private void resolveThrowsTags(MethodScope methScope, boolean reportMissing) {
303 AbstractMethodDeclaration md = methScope.referenceMethod();
304 int throwsTagsLength = this.thrownExceptions == null ? 0 : this.thrownExceptions.length;
306 // If no referenced method (field initializer for example) then report a problem for each throws tag
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;
319 methScope.problemReporter().javadocUnexpectedTag(start, end);
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) {
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
333 while (j<thrownExceptionLength && exceptionBinding != md.thrownExceptions[j].resolvedType) j++;
334 if (j<thrownExceptionLength) {
335 methScope.problemReporter().javadocMissingThrowsTag(md.thrownExceptions[j], md.binding.modifiers);
342 TypeReference[] typeReferences = new TypeReference[throwsTagsLength];
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;
350 if (typeBinding != null && typeBinding.isValidBinding() && typeBinding.isClass()) {
351 typeReferences[maxRef++] = typeRef;
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) {
364 typeReferences[j] = null;
368 if (!found && reportMissing) {
369 if (exceptionBinding != null && exceptionBinding.isValidBinding()) { // flag only valid class name
371 while (k<thrownExceptionLength && exceptionBinding != md.thrownExceptions[k].resolvedType) k++;
372 if (k<thrownExceptionLength) {
373 methScope.problemReporter().javadocMissingThrowsTag(md.thrownExceptions[k], md.binding.modifiers);
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);
392 // If not compatible only complain on unchecked exception
394 !typeRef.resolvedType.isCompatibleWith(methScope.getJavaLangRuntimeException()) &&
395 !typeRef.resolvedType.isCompatibleWith(methScope.getJavaLangError())) {
396 methScope.problemReporter().javadocInvalidThrowsClassName(typeRef, md.binding.modifiers);
404 * Search node with a given staring position in javadoc objects arrays.
406 public ASTNode getNodeStartingAt(int start) {
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) {
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) {
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) {
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) {
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];
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];