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.classfmt.ClassFileConstants;
15 import org.eclipse.jdt.internal.compiler.lookup.*;
16 import org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser;
19 * Node representing a structured Javadoc comment
21 public class Javadoc extends ASTNode {
23 public JavadocSingleNameReference[] paramReferences; // @param
24 public JavadocSingleTypeReference[] paramTypeParameters; // @param
25 public TypeReference[] exceptionReferences; // @throws, @exception
26 public JavadocReturnStatement returnStatement; // @return
27 public Expression[] seeReferences; // @see
28 public boolean inherited = false;
29 // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51600
30 // Store param references for tag with invalid syntax
31 public JavadocSingleNameReference[] invalidParameters; // @param
33 public Javadoc(int sourceStart, int sourceEnd) {
34 this.sourceStart = sourceStart;
35 this.sourceEnd = sourceEnd;
39 * @see org.eclipse.jdt.internal.compiler.ast.ASTNode#print(int, java.lang.StringBuffer)
41 public StringBuffer print(int indent, StringBuffer output) {
42 printIndent(indent, output).append("/**\n"); //$NON-NLS-1$
43 if (this.paramReferences != null) {
44 for (int i = 0, length = this.paramReferences.length; i < length; i++) {
45 printIndent(indent + 1, output).append(" * @param "); //$NON-NLS-1$
46 this.paramReferences[i].print(indent, output).append('\n');
49 if (this.paramTypeParameters != null) {
50 for (int i = 0, length = this.paramTypeParameters.length; i < length; i++) {
51 printIndent(indent + 1, output).append(" * @param <"); //$NON-NLS-1$
52 this.paramTypeParameters[i].print(indent, output).append(">\n"); //$NON-NLS-1$
55 if (this.returnStatement != null) {
56 printIndent(indent + 1, output).append(" * @"); //$NON-NLS-1$
57 this.returnStatement.print(indent, output).append('\n');
59 if (this.exceptionReferences != null) {
60 for (int i = 0, length = this.exceptionReferences.length; i < length; i++) {
61 printIndent(indent + 1, output).append(" * @throws "); //$NON-NLS-1$
62 this.exceptionReferences[i].print(indent, output).append('\n');
65 if (this.seeReferences != null) {
66 for (int i = 0, length = this.seeReferences.length; i < length; i++) {
67 printIndent(indent + 1, output).append(" * @see"); //$NON-NLS-1$
68 this.seeReferences[i].print(indent, output).append('\n');
71 printIndent(indent, output).append(" */\n"); //$NON-NLS-1$
76 * Resolve type javadoc while a class scope
78 public void resolve(ClassScope classScope) {
81 int paramTagsSize = this.paramReferences == null ? 0 : this.paramReferences.length;
82 for (int i = 0; i < paramTagsSize; i++) {
83 JavadocSingleNameReference param = this.paramReferences[i];
84 classScope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd);
86 resolveTypeParameterTags(classScope, true);
89 if (this.returnStatement != null) {
90 classScope.problemReporter().javadocUnexpectedTag(this.returnStatement.sourceStart, this.returnStatement.sourceEnd);
93 // @throws/@exception tags
94 int throwsTagsLength = this.exceptionReferences == null ? 0 : this.exceptionReferences.length;
95 for (int i = 0; i < throwsTagsLength; i++) {
96 TypeReference typeRef = this.exceptionReferences[i];
98 if (typeRef instanceof JavadocSingleTypeReference) {
99 JavadocSingleTypeReference singleRef = (JavadocSingleTypeReference) typeRef;
100 start = singleRef.tagSourceStart;
101 end = singleRef.tagSourceEnd;
102 } else if (typeRef instanceof JavadocQualifiedTypeReference) {
103 JavadocQualifiedTypeReference qualifiedRef = (JavadocQualifiedTypeReference) typeRef;
104 start = qualifiedRef.tagSourceStart;
105 end = qualifiedRef.tagSourceEnd;
107 start = typeRef.sourceStart;
108 end = typeRef.sourceEnd;
110 classScope.problemReporter().javadocUnexpectedTag(start, end);
114 int seeTagsLength = this.seeReferences == null ? 0 : this.seeReferences.length;
115 for (int i = 0; i < seeTagsLength; i++) {
116 resolveReference(this.seeReferences[i], classScope);
121 * Resolve method javadoc while a method scope
123 public void resolve(MethodScope methScope) {
125 // get method declaration
126 AbstractMethodDeclaration methDecl = methScope.referenceMethod();
127 boolean overriding = methDecl == null ? false : (methDecl.binding.modifiers & (AccImplementing | AccOverriding)) != 0;
130 int seeTagsLength = this.seeReferences == null ? 0 : this.seeReferences.length;
131 boolean superRef = false;
132 for (int i = 0; i < seeTagsLength; i++) {
135 resolveReference(this.seeReferences[i], methScope);
137 // see whether we can have a super reference
139 if (methDecl != null && (methDecl.isConstructor() || overriding) && !superRef) {
140 if (this.seeReferences[i] instanceof JavadocMessageSend) {
141 JavadocMessageSend messageSend = (JavadocMessageSend) this.seeReferences[i];
142 // if binding is valid then look if we have a reference to an overriden method/constructor
143 if (messageSend.binding != null && messageSend.binding.isValidBinding()) {
144 if (methDecl.binding.declaringClass.isCompatibleWith(messageSend.actualReceiverType) &&
145 CharOperation.equals(messageSend.selector, methDecl.selector) &&
146 (messageSend.binding.returnType == methDecl.binding.returnType)) {
147 if (messageSend.arguments == null && methDecl.arguments == null) {
150 else if (messageSend.arguments != null && methDecl.arguments != null) {
151 superRef = methDecl.binding.areParametersEqual(messageSend.binding);
156 else if (this.seeReferences[i] instanceof JavadocAllocationExpression) {
157 JavadocAllocationExpression allocationExpr = (JavadocAllocationExpression) this.seeReferences[i];
158 // if binding is valid then look if we have a reference to an overriden method/constructor
159 if (allocationExpr.binding != null && allocationExpr.binding.isValidBinding()) {
160 if (methDecl.binding.declaringClass.isCompatibleWith(allocationExpr.resolvedType)) {
161 if (allocationExpr.arguments == null && methDecl.arguments == null) {
164 else if (allocationExpr.arguments != null && methDecl.arguments != null) {
165 superRef = methDecl.binding.areParametersEqual(allocationExpr.binding);
172 catch (Exception e) {
173 // Something wrong happen, forget super ref...
177 // Store if a reference exists to an overriden method/constructor or the method is in a local type,
178 boolean reportMissing = methDecl == null || !((overriding && this.inherited) || superRef || (methDecl.binding.declaringClass != null && methDecl.binding.declaringClass.isLocalType()));
181 resolveParamTags(methScope, reportMissing);
182 resolveTypeParameterTags(methScope, reportMissing);
185 if (this.returnStatement == null) {
186 if (reportMissing && methDecl != null) {
187 if (methDecl.isMethod()) {
188 MethodDeclaration meth = (MethodDeclaration) methDecl;
189 if (meth.binding.returnType != VoidBinding) {
190 // method with return should have @return tag
191 methScope.problemReporter().javadocMissingReturnTag(meth.returnType.sourceStart, meth.returnType.sourceEnd, methDecl.binding.modifiers);
196 this.returnStatement.resolve(methScope);
199 // @throws/@exception tags
200 resolveThrowsTags(methScope, reportMissing);
202 // Resolve param tags with invalid syntax
203 int length = this.invalidParameters == null ? 0 : this.invalidParameters.length;
204 for (int i = 0; i < length; i++) {
205 this.invalidParameters[i].resolve(methScope, false);
209 private void resolveReference(Expression reference, Scope scope) {
212 switch (scope.kind) {
213 case Scope.METHOD_SCOPE:
214 reference.resolveType((MethodScope)scope);
216 case Scope.CLASS_SCOPE:
217 reference.resolveType((ClassScope)scope);
221 // Verify field references
222 boolean verifyValues = scope.environment().options.sourceLevel >= ClassFileConstants.JDK1_5;
223 if (reference instanceof JavadocFieldReference) {
224 JavadocFieldReference fieldRef = (JavadocFieldReference) reference;
225 int modifiers = fieldRef.binding==null ? -1 : fieldRef.binding.modifiers;
227 // Verify if this is a method reference
228 // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51911
229 if (fieldRef.methodBinding != null) {
230 // cannot refer to method for @value tag
231 if (fieldRef.tagValue == AbstractCommentParser.TAG_VALUE_VALUE) {
232 scope.problemReporter().javadocInvalidValueReference(fieldRef.sourceStart, fieldRef.sourceEnd, modifiers);
234 else if (fieldRef.receiverType != null) {
235 fieldRef.superAccess = scope.enclosingSourceType().isCompatibleWith(fieldRef.receiverType);
236 fieldRef.methodBinding = scope.findMethod((ReferenceBinding)fieldRef.receiverType, fieldRef.token, new TypeBinding[0], fieldRef);
240 // Verify whether field ref should be static or not (for @value tags)
241 else if (verifyValues && fieldRef.binding != null && fieldRef.binding.isValidBinding()) {
242 if (fieldRef.tagValue == AbstractCommentParser.TAG_VALUE_VALUE && !fieldRef.binding.isStatic()) {
243 scope.problemReporter().javadocInvalidValueReference(fieldRef.sourceStart, fieldRef.sourceEnd, modifiers);
248 // If not 1.5 level, verification is finished
249 if (!verifyValues) return;
251 // Verify that message reference are not used for @value tags
252 else if (reference instanceof JavadocMessageSend) {
253 JavadocMessageSend msgSend = (JavadocMessageSend) reference;
254 int modifiers = msgSend.binding==null ? -1 : msgSend.binding.modifiers;
255 if (msgSend.tagValue == AbstractCommentParser.TAG_VALUE_VALUE) { // cannot refer to method for @value tag
256 scope.problemReporter().javadocInvalidValueReference(msgSend.sourceStart, msgSend.sourceEnd, modifiers);
260 // Verify that constructorreference are not used for @value tags
261 else if (reference instanceof JavadocAllocationExpression) {
262 JavadocAllocationExpression alloc = (JavadocAllocationExpression) reference;
263 int modifiers = alloc.binding==null ? -1 : alloc.binding.modifiers;
264 if (alloc.tagValue == AbstractCommentParser.TAG_VALUE_VALUE) { // cannot refer to method for @value tag
265 scope.problemReporter().javadocInvalidValueReference(alloc.sourceStart, alloc.sourceEnd, modifiers);
271 * Resolve @param tags while method scope
273 private void resolveParamTags(MethodScope methScope, boolean reportMissing) {
274 AbstractMethodDeclaration md = methScope.referenceMethod();
275 int paramTagsSize = this.paramReferences == null ? 0 : this.paramReferences.length;
277 // If no referenced method (field initializer for example) then report a problem for each param tag
279 for (int i = 0; i < paramTagsSize; i++) {
280 JavadocSingleNameReference param = this.paramReferences[i];
281 methScope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd);
286 // If no param tags then report a problem for each method argument
287 int argumentsSize = md.arguments == null ? 0 : md.arguments.length;
288 if (paramTagsSize == 0) {
290 for (int i = 0; i < argumentsSize; i++) {
291 Argument arg = md.arguments[i];
292 methScope.problemReporter().javadocMissingParamTag(arg.name, arg.sourceStart, arg.sourceEnd, md.binding.modifiers);
296 LocalVariableBinding[] bindings = new LocalVariableBinding[paramTagsSize];
299 // Scan all @param tags
300 for (int i = 0; i < paramTagsSize; i++) {
301 JavadocSingleNameReference param = this.paramReferences[i];
302 param.resolve(methScope);
303 if (param.binding != null && param.binding.isValidBinding()) {
304 // Verify duplicated tags
305 boolean found = false;
306 for (int j = 0; j < maxBindings && !found; j++) {
307 if (bindings[j] == param.binding) {
308 methScope.problemReporter().javadocDuplicatedParamTag(param.token, param.sourceStart, param.sourceEnd, md.binding.modifiers);
313 bindings[maxBindings++] = (LocalVariableBinding) param.binding;
318 // Look for undocumented arguments
320 for (int i = 0; i < argumentsSize; i++) {
321 Argument arg = md.arguments[i];
322 boolean found = false;
323 for (int j = 0; j < maxBindings && !found; j++) {
324 LocalVariableBinding binding = bindings[j];
325 if (arg.binding == binding) {
330 methScope.problemReporter().javadocMissingParamTag(arg.name, arg.sourceStart, arg.sourceEnd, md.binding.modifiers);
338 * Resolve @param tags for type parameters
340 private void resolveTypeParameterTags(Scope scope, boolean reportMissing) {
341 int paramTypeParamLength = this.paramTypeParameters == null ? 0 : this.paramTypeParameters.length;
343 // Get declaration infos
344 TypeDeclaration typeDeclaration = null;
345 AbstractMethodDeclaration methodDeclaration = null;
346 TypeVariableBinding[] typeVariables = null;
348 switch (scope.kind) {
349 case Scope.METHOD_SCOPE:
350 methodDeclaration = ((MethodScope)scope).referenceMethod();
351 // If no referenced method (field initializer for example) then report a problem for each param tag
352 if (methodDeclaration == null) {
353 for (int i = 0; i < paramTypeParamLength; i++) {
354 JavadocSingleNameReference param = this.paramReferences[i];
355 scope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd);
359 typeVariables = methodDeclaration.binding.typeVariables;
360 modifiers = methodDeclaration.binding.modifiers;
362 case Scope.CLASS_SCOPE:
363 typeDeclaration = ((ClassScope) scope).referenceContext;
364 typeVariables = typeDeclaration.binding.typeVariables;
365 modifiers = typeDeclaration.binding.modifiers;
369 // If no type variables then report a problem for each param type parameter tag
370 if (typeVariables == null || typeVariables.length == 0) {
371 for (int i = 0; i < paramTypeParamLength; i++) {
372 JavadocSingleTypeReference param = this.paramTypeParameters[i];
373 scope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd);
378 // If no param tags then report a problem for each declaration type parameter
379 TypeParameter[] parameters = typeDeclaration==null ? methodDeclaration.typeParameters() : typeDeclaration.typeParameters;
380 int typeParametersLength = parameters == null ? 0 : parameters.length;
381 if (paramTypeParamLength == 0) {
383 for (int i = 0, l=parameters.length; i<l; i++) {
384 scope.problemReporter().javadocMissingParamTag(parameters[i].name, parameters[i].sourceStart, parameters[i].sourceEnd, modifiers);
387 // Otherwise verify that all param tags match type parameters
388 } else if (typeVariables.length == typeParametersLength) {
389 TypeVariableBinding[] bindings = new TypeVariableBinding[paramTypeParamLength];
392 // Scan all @param tags
393 for (int i = 0; i < paramTypeParamLength; i++) {
394 JavadocSingleTypeReference param = this.paramTypeParameters[i];
395 TypeBinding paramBindind = param.internalResolveType(scope);
396 if (paramBindind != null && paramBindind.isValidBinding()) {
397 if (paramBindind.isTypeVariable()) {
398 // Verify duplicated tags
399 boolean duplicate = false;
400 for (int j = 0; j < maxBindings && !duplicate; j++) {
401 if (bindings[j] == param.resolvedType) {
402 scope.problemReporter().javadocDuplicatedParamTag(param.token, param.sourceStart, param.sourceEnd, modifiers);
407 bindings[maxBindings++] = (TypeVariableBinding) param.resolvedType;
410 scope.problemReporter().javadocUndeclaredParamTagName(param.token, param.sourceStart, param.sourceEnd, modifiers);
415 // Look for undocumented type parameters
417 for (int i = 0; i < typeParametersLength; i++) {
418 TypeParameter parameter = parameters[i];
419 boolean found = false;
420 for (int j = 0; j < maxBindings && !found; j++) {
421 if (parameter.binding == bindings[j]) {
426 scope.problemReporter().javadocMissingParamTag(parameter.name, parameter.sourceStart, parameter.sourceEnd, modifiers);
434 * Resolve @throws/@exception tags while method scope
436 private void resolveThrowsTags(MethodScope methScope, boolean reportMissing) {
437 AbstractMethodDeclaration md = methScope.referenceMethod();
438 int throwsTagsLength = this.exceptionReferences == null ? 0 : this.exceptionReferences.length;
440 // If no referenced method (field initializer for example) then report a problem for each throws tag
442 for (int i = 0; i < throwsTagsLength; i++) {
443 TypeReference typeRef = this.exceptionReferences[i];
444 int start = typeRef.sourceStart;
445 int end = typeRef.sourceEnd;
446 if (typeRef instanceof JavadocQualifiedTypeReference) {
447 start = ((JavadocQualifiedTypeReference) typeRef).tagSourceStart;
448 end = ((JavadocQualifiedTypeReference) typeRef).tagSourceEnd;
449 } else if (typeRef instanceof JavadocSingleTypeReference) {
450 start = ((JavadocSingleTypeReference) typeRef).tagSourceStart;
451 end = ((JavadocSingleTypeReference) typeRef).tagSourceEnd;
453 methScope.problemReporter().javadocUnexpectedTag(start, end);
458 // If no throws tags then report a problem for each method thrown exception
459 int boundExceptionLength = (md.binding == null || md.binding.thrownExceptions == null) ? 0 : md.binding.thrownExceptions.length;
460 int thrownExceptionLength = md.thrownExceptions == null ? 0 : md.thrownExceptions.length;
461 if (throwsTagsLength == 0) {
463 for (int i = 0; i < boundExceptionLength; i++) {
464 ReferenceBinding exceptionBinding = md.binding.thrownExceptions[i];
465 if (exceptionBinding != null && exceptionBinding.isValidBinding()) { // flag only valid class name
467 while (j<thrownExceptionLength && exceptionBinding != md.thrownExceptions[j].resolvedType) j++;
468 if (j<thrownExceptionLength) {
469 methScope.problemReporter().javadocMissingThrowsTag(md.thrownExceptions[j], md.binding.modifiers);
476 TypeReference[] typeReferences = new TypeReference[throwsTagsLength];
478 // Scan all @throws tags
479 for (int i = 0; i < throwsTagsLength; i++) {
480 TypeReference typeRef = this.exceptionReferences[i];
481 typeRef.resolve(methScope);
482 TypeBinding typeBinding = typeRef.resolvedType;
484 if (typeBinding != null && typeBinding.isValidBinding() && typeBinding.isClass()) {
485 typeReferences[maxRef++] = typeRef;
489 // Look for undocumented thrown exception
490 for (int i = 0; i < boundExceptionLength; i++) {
491 ReferenceBinding exceptionBinding = md.binding.thrownExceptions[i];
492 boolean found = false;
493 for (int j = 0; j < maxRef && !found; j++) {
494 if (typeReferences[j] != null) {
495 TypeBinding typeBinding = typeReferences[j].resolvedType;
496 if (exceptionBinding == typeBinding) {
498 typeReferences[j] = null;
502 if (!found && reportMissing) {
503 if (exceptionBinding != null && exceptionBinding.isValidBinding()) { // flag only valid class name
505 while (k<thrownExceptionLength && exceptionBinding != md.thrownExceptions[k].resolvedType) k++;
506 if (k<thrownExceptionLength) {
507 methScope.problemReporter().javadocMissingThrowsTag(md.thrownExceptions[k], md.binding.modifiers);
513 // Verify additional @throws tags
514 for (int i = 0; i < maxRef; i++) {
515 TypeReference typeRef = typeReferences[i];
516 if (typeRef != null) {
517 boolean compatible = false;
518 // thrown exceptions subclasses are accepted
519 for (int j = 0; j<thrownExceptionLength && !compatible; j++) {
520 TypeBinding exceptionBinding = md.thrownExceptions[j].resolvedType;
521 if (exceptionBinding != null) {
522 compatible = typeRef.resolvedType.isCompatibleWith(exceptionBinding);
526 // If not compatible only complain on unchecked exception
528 !typeRef.resolvedType.isCompatibleWith(methScope.getJavaLangRuntimeException()) &&
529 !typeRef.resolvedType.isCompatibleWith(methScope.getJavaLangError())) {
530 methScope.problemReporter().javadocInvalidThrowsClassName(typeRef, md.binding.modifiers);
538 * Search node with a given staring position in javadoc objects arrays.
540 public ASTNode getNodeStartingAt(int start) {
543 if (this.paramReferences != null) {
544 length = this.paramReferences.length;
545 for (int i=0; i<length; i++) {
546 JavadocSingleNameReference param = this.paramReferences[i];
547 if (param.sourceStart==start) {
552 // array of invalid syntax tags parameters
553 if (this.invalidParameters != null) {
554 length = this.invalidParameters.length;
555 for (int i=0; i<length; i++) {
556 JavadocSingleNameReference param = this.invalidParameters[i];
557 if (param.sourceStart==start) {
562 // type parameters array
563 if (this.paramTypeParameters != null) {
564 length = this.paramTypeParameters.length;
565 for (int i=0; i<length; i++) {
566 JavadocSingleTypeReference param = this.paramTypeParameters[i];
567 if (param.sourceStart==start) {
572 // thrown exception array
573 if (this.exceptionReferences != null) {
574 length = this.exceptionReferences.length;
575 for (int i=0; i<length; i++) {
576 TypeReference typeRef = this.exceptionReferences[i];
577 if (typeRef.sourceStart==start) {
583 if (this.seeReferences != null) {
584 length = this.seeReferences.length;
585 for (int i=0; i<length; i++) {
586 org.eclipse.jdt.internal.compiler.ast.Expression expression = this.seeReferences[i];
587 if (expression.sourceStart==start) {
589 } else if (expression instanceof JavadocAllocationExpression) {
590 JavadocAllocationExpression allocationExpr = (JavadocAllocationExpression) this.seeReferences[i];
591 // if binding is valid then look at arguments
592 if (allocationExpr.binding != null && allocationExpr.binding.isValidBinding()) {
593 if (allocationExpr.arguments != null) {
594 for (int j=0, l=allocationExpr.arguments.length; j<l; j++) {
595 if (allocationExpr.arguments[j].sourceStart == start) {
596 return allocationExpr.arguments[j];
601 } else if (expression instanceof JavadocMessageSend) {
602 JavadocMessageSend messageSend = (JavadocMessageSend) this.seeReferences[i];
603 // if binding is valid then look at arguments
604 if (messageSend.binding != null && messageSend.binding.isValidBinding()) {
605 if (messageSend.arguments != null) {
606 for (int j=0, l=messageSend.arguments.length; j<l; j++) {
607 if (messageSend.arguments[j].sourceStart == start) {
608 return messageSend.arguments[j];