package org.eclipse.jdt.internal.compiler.ast;
import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.*;
+import org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser;
/**
* Node representing a structured Javadoc comment
*/
public class Javadoc extends ASTNode {
- public JavadocSingleNameReference[] parameters; // @param
- public TypeReference[] thrownExceptions; // @throws, @exception
+ public JavadocSingleNameReference[] paramReferences; // @param
+ public JavadocSingleTypeReference[] paramTypeParameters; // @param
+ public TypeReference[] exceptionReferences; // @throws, @exception
public JavadocReturnStatement returnStatement; // @return
- public Expression[] references; // @see
+ public Expression[] seeReferences; // @see
public boolean inherited = false;
// bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51600
// Store param references for tag with invalid syntax
*/
public StringBuffer print(int indent, StringBuffer output) {
printIndent(indent, output).append("/**\n"); //$NON-NLS-1$
- if (this.parameters != null) {
- for (int i = 0, length = this.parameters.length; i < length; i++) {
+ if (this.paramReferences != null) {
+ for (int i = 0, length = this.paramReferences.length; i < length; i++) {
printIndent(indent + 1, output).append(" * @param "); //$NON-NLS-1$
- this.parameters[i].print(indent, output).append('\n');
+ this.paramReferences[i].print(indent, output).append('\n');
+ }
+ }
+ if (this.paramTypeParameters != null) {
+ for (int i = 0, length = this.paramTypeParameters.length; i < length; i++) {
+ printIndent(indent + 1, output).append(" * @param <"); //$NON-NLS-1$
+ this.paramTypeParameters[i].print(indent, output).append(">\n"); //$NON-NLS-1$
}
}
if (this.returnStatement != null) {
- printIndent(indent + 1, output).append(" * @return\n"); //$NON-NLS-1$
+ printIndent(indent + 1, output).append(" * @"); //$NON-NLS-1$
+ this.returnStatement.print(indent, output).append('\n');
}
- if (this.thrownExceptions != null) {
- for (int i = 0, length = this.thrownExceptions.length; i < length; i++) {
+ if (this.exceptionReferences != null) {
+ for (int i = 0, length = this.exceptionReferences.length; i < length; i++) {
printIndent(indent + 1, output).append(" * @throws "); //$NON-NLS-1$
- this.thrownExceptions[i].print(indent, output).append('\n');
+ this.exceptionReferences[i].print(indent, output).append('\n');
}
}
- if (this.references != null) {
- for (int i = 0, length = this.references.length; i < length; i++) {
+ if (this.seeReferences != null) {
+ for (int i = 0, length = this.seeReferences.length; i < length; i++) {
printIndent(indent + 1, output).append(" * @see"); //$NON-NLS-1$
- this.references[i].print(indent, output).append('\n');
+ this.seeReferences[i].print(indent, output).append('\n');
}
}
printIndent(indent, output).append(" */\n"); //$NON-NLS-1$
* Resolve type javadoc while a class scope
*/
public void resolve(ClassScope classScope) {
-
// @param tags
- int paramTagsSize = this.parameters == null ? 0 : this.parameters.length;
+ int paramTagsSize = this.paramReferences == null ? 0 : this.paramReferences.length;
for (int i = 0; i < paramTagsSize; i++) {
- JavadocSingleNameReference param = this.parameters[i];
+ JavadocSingleNameReference param = this.paramReferences[i];
classScope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd);
}
+ resolveTypeParameterTags(classScope, true);
// @return tags
if (this.returnStatement != null) {
}
// @throws/@exception tags
- int throwsTagsLength = this.thrownExceptions == null ? 0 : this.thrownExceptions.length;
+ int throwsTagsLength = this.exceptionReferences == null ? 0 : this.exceptionReferences.length;
for (int i = 0; i < throwsTagsLength; i++) {
- TypeReference typeRef = this.thrownExceptions[i];
+ TypeReference typeRef = this.exceptionReferences[i];
int start, end;
if (typeRef instanceof JavadocSingleTypeReference) {
JavadocSingleTypeReference singleRef = (JavadocSingleTypeReference) typeRef;
}
// @see tags
- int seeTagsLength = this.references == null ? 0 : this.references.length;
+ int seeTagsLength = this.seeReferences == null ? 0 : this.seeReferences.length;
for (int i = 0; i < seeTagsLength; i++) {
-
- // Resolve reference
- this.references[i].resolveType(classScope);
-
- // Some unbound field reference might be changed to message send
- // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51911
- if (this.references[i] instanceof JavadocFieldReference) {
- JavadocFieldReference fieldRef = (JavadocFieldReference) this.references[i];
- if (fieldRef.receiverType != null && fieldRef.binding == null) { // binding was reset in case of valid method reference
- // TODO (frederic) post 3.0 - avoid new instanciation of Compiler AST node
- JavadocMessageSend msgSend = new JavadocMessageSend(fieldRef.token, fieldRef.nameSourcePosition);
- msgSend.receiver = fieldRef.receiver;
- msgSend.receiverType = fieldRef.receiverType;
- msgSend.qualifyingType = fieldRef.receiverType;
- msgSend.superAccess = classScope.enclosingSourceType().isCompatibleWith(msgSend.receiverType);
- msgSend.binding = classScope.findMethod((ReferenceBinding)msgSend.receiverType, msgSend.selector, new TypeBinding[0], msgSend);
- this.references[i] = msgSend;
- }
- }
+ resolveReference(this.seeReferences[i], classScope);
}
}
// get method declaration
AbstractMethodDeclaration methDecl = methScope.referenceMethod();
- boolean overriding = methDecl == null ? false : (methDecl.binding.modifiers & (AccImplementing+AccOverriding)) != 0;
+ boolean overriding = methDecl == null ? false : (methDecl.binding.modifiers & (AccImplementing | AccOverriding)) != 0;
// @see tags
- int seeTagsLength = this.references == null ? 0 : this.references.length;
+ int seeTagsLength = this.seeReferences == null ? 0 : this.seeReferences.length;
boolean superRef = false;
for (int i = 0; i < seeTagsLength; i++) {
// Resolve reference
- this.references[i].resolveType(methScope);
+ resolveReference(this.seeReferences[i], methScope);
- // Some unbound field reference might be changed to message send
- // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51911
- if (this.references[i] instanceof JavadocFieldReference) {
- JavadocFieldReference fieldRef = (JavadocFieldReference) this.references[i];
- if (fieldRef.receiverType != null && fieldRef.binding == null) { // binding was reset in case of valid method reference
- // TODO (frederic) post 3.0 - avoid new instanciation of Compiler AST node
- JavadocMessageSend msgSend = new JavadocMessageSend(fieldRef.token, fieldRef.nameSourcePosition);
- msgSend.receiver = fieldRef.receiver;
- msgSend.receiverType = fieldRef.receiverType;
- msgSend.qualifyingType = fieldRef.receiverType;
- msgSend.superAccess = methScope.enclosingSourceType().isCompatibleWith(msgSend.receiverType);
- msgSend.binding = methScope.findMethod((ReferenceBinding)msgSend.receiverType, msgSend.selector, new TypeBinding[0], msgSend);
- this.references[i] = msgSend;
- }
- }
-
// see whether we can have a super reference
try {
if (methDecl != null && (methDecl.isConstructor() || overriding) && !superRef) {
- if (this.references[i] instanceof JavadocMessageSend) {
- JavadocMessageSend messageSend = (JavadocMessageSend) this.references[i];
+ if (this.seeReferences[i] instanceof JavadocMessageSend) {
+ JavadocMessageSend messageSend = (JavadocMessageSend) this.seeReferences[i];
// if binding is valid then look if we have a reference to an overriden method/constructor
if (messageSend.binding != null && messageSend.binding.isValidBinding()) {
- if (methDecl.binding.declaringClass.isCompatibleWith(messageSend.receiverType) &&
+ if (methDecl.binding.declaringClass.isCompatibleWith(messageSend.actualReceiverType) &&
CharOperation.equals(messageSend.selector, methDecl.selector) &&
(messageSend.binding.returnType == methDecl.binding.returnType)) {
if (messageSend.arguments == null && methDecl.arguments == null) {
}
}
}
- else if (this.references[i] instanceof JavadocAllocationExpression) {
- JavadocAllocationExpression allocationExpr = (JavadocAllocationExpression) this.references[i];
+ else if (this.seeReferences[i] instanceof JavadocAllocationExpression) {
+ JavadocAllocationExpression allocationExpr = (JavadocAllocationExpression) this.seeReferences[i];
// if binding is valid then look if we have a reference to an overriden method/constructor
if (allocationExpr.binding != null && allocationExpr.binding.isValidBinding()) {
if (methDecl.binding.declaringClass.isCompatibleWith(allocationExpr.resolvedType)) {
// @param tags
resolveParamTags(methScope, reportMissing);
+ resolveTypeParameterTags(methScope, reportMissing);
// @return tags
if (this.returnStatement == null) {
if (reportMissing && methDecl != null) {
- if (!methDecl.isConstructor() && !methDecl.isClinit()) {
+ if (methDecl.isMethod()) {
MethodDeclaration meth = (MethodDeclaration) methDecl;
if (meth.binding.returnType != VoidBinding) {
// method with return should have @return tag
}
}
+ private void resolveReference(Expression reference, Scope scope) {
+
+ // Perform resolve
+ switch (scope.kind) {
+ case Scope.METHOD_SCOPE:
+ reference.resolveType((MethodScope)scope);
+ break;
+ case Scope.CLASS_SCOPE:
+ reference.resolveType((ClassScope)scope);
+ break;
+ }
+
+ // Verify field references
+ boolean verifyValues = scope.environment().options.sourceLevel >= ClassFileConstants.JDK1_5;
+ if (reference instanceof JavadocFieldReference) {
+ JavadocFieldReference fieldRef = (JavadocFieldReference) reference;
+ int modifiers = fieldRef.binding==null ? -1 : fieldRef.binding.modifiers;
+
+ // Verify if this is a method reference
+ // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51911
+ if (fieldRef.methodBinding != null) {
+ // cannot refer to method for @value tag
+ if (fieldRef.tagValue == AbstractCommentParser.TAG_VALUE_VALUE) {
+ scope.problemReporter().javadocInvalidValueReference(fieldRef.sourceStart, fieldRef.sourceEnd, modifiers);
+ }
+ else if (fieldRef.receiverType != null) {
+ fieldRef.superAccess = scope.enclosingSourceType().isCompatibleWith(fieldRef.receiverType);
+ fieldRef.methodBinding = scope.findMethod((ReferenceBinding)fieldRef.receiverType, fieldRef.token, new TypeBinding[0], fieldRef);
+ }
+ }
+
+ // Verify whether field ref should be static or not (for @value tags)
+ else if (verifyValues && fieldRef.binding != null && fieldRef.binding.isValidBinding()) {
+ if (fieldRef.tagValue == AbstractCommentParser.TAG_VALUE_VALUE && !fieldRef.binding.isStatic()) {
+ scope.problemReporter().javadocInvalidValueReference(fieldRef.sourceStart, fieldRef.sourceEnd, modifiers);
+ }
+ }
+ }
+
+ // If not 1.5 level, verification is finished
+ if (!verifyValues) return;
+
+ // Verify that message reference are not used for @value tags
+ else if (reference instanceof JavadocMessageSend) {
+ JavadocMessageSend msgSend = (JavadocMessageSend) reference;
+ int modifiers = msgSend.binding==null ? -1 : msgSend.binding.modifiers;
+ if (msgSend.tagValue == AbstractCommentParser.TAG_VALUE_VALUE) { // cannot refer to method for @value tag
+ scope.problemReporter().javadocInvalidValueReference(msgSend.sourceStart, msgSend.sourceEnd, modifiers);
+ }
+ }
+
+ // Verify that constructorreference are not used for @value tags
+ else if (reference instanceof JavadocAllocationExpression) {
+ JavadocAllocationExpression alloc = (JavadocAllocationExpression) reference;
+ int modifiers = alloc.binding==null ? -1 : alloc.binding.modifiers;
+ if (alloc.tagValue == AbstractCommentParser.TAG_VALUE_VALUE) { // cannot refer to method for @value tag
+ scope.problemReporter().javadocInvalidValueReference(alloc.sourceStart, alloc.sourceEnd, modifiers);
+ }
+ }
+ }
+
/*
* Resolve @param tags while method scope
*/
private void resolveParamTags(MethodScope methScope, boolean reportMissing) {
AbstractMethodDeclaration md = methScope.referenceMethod();
- int paramTagsSize = this.parameters == null ? 0 : this.parameters.length;
+ int paramTagsSize = this.paramReferences == null ? 0 : this.paramReferences.length;
// If no referenced method (field initializer for example) then report a problem for each param tag
if (md == null) {
for (int i = 0; i < paramTagsSize; i++) {
- JavadocSingleNameReference param = this.parameters[i];
+ JavadocSingleNameReference param = this.paramReferences[i];
methScope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd);
}
return;
if (reportMissing) {
for (int i = 0; i < argumentsSize; i++) {
Argument arg = md.arguments[i];
- methScope.problemReporter().javadocMissingParamTag(arg, md.binding.modifiers);
+ methScope.problemReporter().javadocMissingParamTag(arg.name, arg.sourceStart, arg.sourceEnd, md.binding.modifiers);
}
}
} else {
// Scan all @param tags
for (int i = 0; i < paramTagsSize; i++) {
- JavadocSingleNameReference param = this.parameters[i];
+ JavadocSingleNameReference param = this.paramReferences[i];
param.resolve(methScope);
if (param.binding != null && param.binding.isValidBinding()) {
// Verify duplicated tags
boolean found = false;
for (int j = 0; j < maxBindings && !found; j++) {
if (bindings[j] == param.binding) {
- methScope.problemReporter().javadocDuplicatedParamTag(param, md.binding.modifiers);
+ methScope.problemReporter().javadocDuplicatedParamTag(param.token, param.sourceStart, param.sourceEnd, md.binding.modifiers);
found = true;
}
}
}
}
if (!found) {
- methScope.problemReporter().javadocMissingParamTag(arg, md.binding.modifiers);
+ methScope.problemReporter().javadocMissingParamTag(arg.name, arg.sourceStart, arg.sourceEnd, md.binding.modifiers);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Resolve @param tags for type parameters
+ */
+ private void resolveTypeParameterTags(Scope scope, boolean reportMissing) {
+ int paramTypeParamLength = this.paramTypeParameters == null ? 0 : this.paramTypeParameters.length;
+
+ // Get declaration infos
+ TypeDeclaration typeDeclaration = null;
+ AbstractMethodDeclaration methodDeclaration = null;
+ TypeVariableBinding[] typeVariables = null;
+ int modifiers = -1;
+ switch (scope.kind) {
+ case Scope.METHOD_SCOPE:
+ methodDeclaration = ((MethodScope)scope).referenceMethod();
+ // If no referenced method (field initializer for example) then report a problem for each param tag
+ if (methodDeclaration == null) {
+ for (int i = 0; i < paramTypeParamLength; i++) {
+ JavadocSingleNameReference param = this.paramReferences[i];
+ scope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd);
+ }
+ return;
+ }
+ typeVariables = methodDeclaration.binding.typeVariables;
+ modifiers = methodDeclaration.binding.modifiers;
+ break;
+ case Scope.CLASS_SCOPE:
+ typeDeclaration = ((ClassScope) scope).referenceContext;
+ typeVariables = typeDeclaration.binding.typeVariables;
+ modifiers = typeDeclaration.binding.modifiers;
+ break;
+ }
+
+ // If no type variables then report a problem for each param type parameter tag
+ if (typeVariables == null || typeVariables.length == 0) {
+ for (int i = 0; i < paramTypeParamLength; i++) {
+ JavadocSingleTypeReference param = this.paramTypeParameters[i];
+ scope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd);
+ }
+ return;
+ }
+
+ // If no param tags then report a problem for each declaration type parameter
+ TypeParameter[] parameters = typeDeclaration==null ? methodDeclaration.typeParameters() : typeDeclaration.typeParameters;
+ int typeParametersLength = parameters == null ? 0 : parameters.length;
+ if (paramTypeParamLength == 0) {
+ if (reportMissing) {
+ for (int i = 0, l=parameters.length; i<l; i++) {
+ scope.problemReporter().javadocMissingParamTag(parameters[i].name, parameters[i].sourceStart, parameters[i].sourceEnd, modifiers);
+ }
+ }
+ // Otherwise verify that all param tags match type parameters
+ } else if (typeVariables.length == typeParametersLength) {
+ TypeVariableBinding[] bindings = new TypeVariableBinding[paramTypeParamLength];
+ int maxBindings = 0;
+
+ // Scan all @param tags
+ for (int i = 0; i < paramTypeParamLength; i++) {
+ JavadocSingleTypeReference param = this.paramTypeParameters[i];
+ TypeBinding paramBindind = param.internalResolveType(scope);
+ if (paramBindind != null && paramBindind.isValidBinding()) {
+ if (paramBindind.isTypeVariable()) {
+ // Verify duplicated tags
+ boolean duplicate = false;
+ for (int j = 0; j < maxBindings && !duplicate; j++) {
+ if (bindings[j] == param.resolvedType) {
+ scope.problemReporter().javadocDuplicatedParamTag(param.token, param.sourceStart, param.sourceEnd, modifiers);
+ duplicate = true;
+ }
+ }
+ if (!duplicate) {
+ bindings[maxBindings++] = (TypeVariableBinding) param.resolvedType;
+ }
+ } else {
+ scope.problemReporter().javadocUndeclaredParamTagName(param.token, param.sourceStart, param.sourceEnd, modifiers);
+ }
+ }
+ }
+
+ // Look for undocumented type parameters
+ if (reportMissing) {
+ for (int i = 0; i < typeParametersLength; i++) {
+ TypeParameter parameter = parameters[i];
+ boolean found = false;
+ for (int j = 0; j < maxBindings && !found; j++) {
+ if (parameter.binding == bindings[j]) {
+ found = true;
+ }
+ }
+ if (!found) {
+ scope.problemReporter().javadocMissingParamTag(parameter.name, parameter.sourceStart, parameter.sourceEnd, modifiers);
}
}
}
*/
private void resolveThrowsTags(MethodScope methScope, boolean reportMissing) {
AbstractMethodDeclaration md = methScope.referenceMethod();
- int throwsTagsLength = this.thrownExceptions == null ? 0 : this.thrownExceptions.length;
+ int throwsTagsLength = this.exceptionReferences == null ? 0 : this.exceptionReferences.length;
// If no referenced method (field initializer for example) then report a problem for each throws tag
if (md == null) {
for (int i = 0; i < throwsTagsLength; i++) {
- TypeReference typeRef = this.thrownExceptions[i];
+ TypeReference typeRef = this.exceptionReferences[i];
int start = typeRef.sourceStart;
int end = typeRef.sourceEnd;
if (typeRef instanceof JavadocQualifiedTypeReference) {
// Scan all @throws tags
for (int i = 0; i < throwsTagsLength; i++) {
- TypeReference typeRef = this.thrownExceptions[i];
+ TypeReference typeRef = this.exceptionReferences[i];
typeRef.resolve(methScope);
TypeBinding typeBinding = typeRef.resolvedType;
* Search node with a given staring position in javadoc objects arrays.
*/
public ASTNode getNodeStartingAt(int start) {
+ int length = 0;
// parameters array
- if (this.parameters != null) {
- for (int i=0; i<this.parameters.length; i++) {
- JavadocSingleNameReference param = this.parameters[i];
+ if (this.paramReferences != null) {
+ length = this.paramReferences.length;
+ for (int i=0; i<length; i++) {
+ JavadocSingleNameReference param = this.paramReferences[i];
if (param.sourceStart==start) {
return param;
}
}
// array of invalid syntax tags parameters
if (this.invalidParameters != null) {
- for (int i=0; i<this.invalidParameters.length; i++) {
+ length = this.invalidParameters.length;
+ for (int i=0; i<length; i++) {
JavadocSingleNameReference param = this.invalidParameters[i];
if (param.sourceStart==start) {
return param;
}
}
}
+ // type parameters array
+ if (this.paramTypeParameters != null) {
+ length = this.paramTypeParameters.length;
+ for (int i=0; i<length; i++) {
+ JavadocSingleTypeReference param = this.paramTypeParameters[i];
+ if (param.sourceStart==start) {
+ return param;
+ }
+ }
+ }
// thrown exception array
- if (this.thrownExceptions != null) {
- for (int i=0; i<this.thrownExceptions.length; i++) {
- TypeReference typeRef = this.thrownExceptions[i];
+ if (this.exceptionReferences != null) {
+ length = this.exceptionReferences.length;
+ for (int i=0; i<length; i++) {
+ TypeReference typeRef = this.exceptionReferences[i];
if (typeRef.sourceStart==start) {
return typeRef;
}
}
}
// references array
- if (this.references != null) {
- for (int i=0; i<this.references.length; i++) {
- org.eclipse.jdt.internal.compiler.ast.Expression expression = this.references[i];
+ if (this.seeReferences != null) {
+ length = this.seeReferences.length;
+ for (int i=0; i<length; i++) {
+ org.eclipse.jdt.internal.compiler.ast.Expression expression = this.seeReferences[i];
if (expression.sourceStart==start) {
return expression;
} else if (expression instanceof JavadocAllocationExpression) {
- JavadocAllocationExpression allocationExpr = (JavadocAllocationExpression) this.references[i];
+ JavadocAllocationExpression allocationExpr = (JavadocAllocationExpression) this.seeReferences[i];
// if binding is valid then look at arguments
if (allocationExpr.binding != null && allocationExpr.binding.isValidBinding()) {
if (allocationExpr.arguments != null) {
- for (int j=0; j<allocationExpr.arguments.length; j++) {
+ for (int j=0, l=allocationExpr.arguments.length; j<l; j++) {
if (allocationExpr.arguments[j].sourceStart == start) {
return allocationExpr.arguments[j];
}
}
}
} else if (expression instanceof JavadocMessageSend) {
- JavadocMessageSend messageSend = (JavadocMessageSend) this.references[i];
+ JavadocMessageSend messageSend = (JavadocMessageSend) this.seeReferences[i];
// if binding is valid then look at arguments
if (messageSend.binding != null && messageSend.binding.isValidBinding()) {
if (messageSend.arguments != null) {
- for (int j=0; j<messageSend.arguments.length; j++) {
+ for (int j=0, l=messageSend.arguments.length; j<l; j++) {
if (messageSend.arguments[j].sourceStart == start) {
return messageSend.arguments[j];
}