0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.compiler.ast;
0011:
0012: import org.eclipse.jdt.core.compiler.CharOperation;
0013: import org.eclipse.jdt.internal.compiler.ASTVisitor;
0014: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
0015: import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
0016: import org.eclipse.jdt.internal.compiler.lookup.*;
0017: import org.eclipse.jdt.internal.compiler.parser.JavadocTagConstants;
0018:
0019: /**
0020: * Node representing a structured Javadoc comment
0021: */
0022: public class Javadoc extends ASTNode {
0023:
0024: public JavadocSingleNameReference[] paramReferences; // @param
0025: public JavadocSingleTypeReference[] paramTypeParameters; // @param
0026: public TypeReference[] exceptionReferences; // @throws, @exception
0027: public JavadocReturnStatement returnStatement; // @return
0028: public Expression[] seeReferences; // @see
0029: public long inheritedPositions = -1;
0030: // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51600
0031: // Store param references for tag with invalid syntax
0032: public JavadocSingleNameReference[] invalidParameters; // @param
0033: // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=153399
0034: // Store value tag positions
0035: public long valuePositions = -1;
0036:
0037: public Javadoc(int sourceStart, int sourceEnd) {
0038: this .sourceStart = sourceStart;
0039: this .sourceEnd = sourceEnd;
0040: }
0041:
0042: /**
0043: * Returns whether a type can be seen at a given visibility level or not.
0044: *
0045: * @param visibility Level of visiblity allowed to see references
0046: * @param modifiers modifiers of java element to be seen
0047: * @return true if the type can be seen, false otherwise
0048: */
0049: boolean canBeSeen(int visibility, int modifiers) {
0050: if (modifiers < 0)
0051: return true;
0052: switch (modifiers & ExtraCompilerModifiers.AccVisibilityMASK) {
0053: case ClassFileConstants.AccPublic:
0054: return true;
0055: case ClassFileConstants.AccProtected:
0056: return (visibility != ClassFileConstants.AccPublic);
0057: case ClassFileConstants.AccDefault:
0058: return (visibility == ClassFileConstants.AccDefault || visibility == ClassFileConstants.AccPrivate);
0059: case ClassFileConstants.AccPrivate:
0060: return (visibility == ClassFileConstants.AccPrivate);
0061: }
0062: return true;
0063: }
0064:
0065: /*
0066: * Search node with a given staring position in javadoc objects arrays.
0067: */
0068: public ASTNode getNodeStartingAt(int start) {
0069: int length = 0;
0070: // parameters array
0071: if (this .paramReferences != null) {
0072: length = this .paramReferences.length;
0073: for (int i = 0; i < length; i++) {
0074: JavadocSingleNameReference param = this .paramReferences[i];
0075: if (param.sourceStart == start) {
0076: return param;
0077: }
0078: }
0079: }
0080: // array of invalid syntax tags parameters
0081: if (this .invalidParameters != null) {
0082: length = this .invalidParameters.length;
0083: for (int i = 0; i < length; i++) {
0084: JavadocSingleNameReference param = this .invalidParameters[i];
0085: if (param.sourceStart == start) {
0086: return param;
0087: }
0088: }
0089: }
0090: // type parameters array
0091: if (this .paramTypeParameters != null) {
0092: length = this .paramTypeParameters.length;
0093: for (int i = 0; i < length; i++) {
0094: JavadocSingleTypeReference param = this .paramTypeParameters[i];
0095: if (param.sourceStart == start) {
0096: return param;
0097: }
0098: }
0099: }
0100: // thrown exception array
0101: if (this .exceptionReferences != null) {
0102: length = this .exceptionReferences.length;
0103: for (int i = 0; i < length; i++) {
0104: TypeReference typeRef = this .exceptionReferences[i];
0105: if (typeRef.sourceStart == start) {
0106: return typeRef;
0107: }
0108: }
0109: }
0110: // references array
0111: if (this .seeReferences != null) {
0112: length = this .seeReferences.length;
0113: for (int i = 0; i < length; i++) {
0114: org.eclipse.jdt.internal.compiler.ast.Expression expression = this .seeReferences[i];
0115: if (expression.sourceStart == start) {
0116: return expression;
0117: } else if (expression instanceof JavadocAllocationExpression) {
0118: JavadocAllocationExpression allocationExpr = (JavadocAllocationExpression) this .seeReferences[i];
0119: // if binding is valid then look at arguments
0120: if (allocationExpr.binding != null
0121: && allocationExpr.binding.isValidBinding()) {
0122: if (allocationExpr.arguments != null) {
0123: for (int j = 0, l = allocationExpr.arguments.length; j < l; j++) {
0124: if (allocationExpr.arguments[j].sourceStart == start) {
0125: return allocationExpr.arguments[j];
0126: }
0127: }
0128: }
0129: }
0130: } else if (expression instanceof JavadocMessageSend) {
0131: JavadocMessageSend messageSend = (JavadocMessageSend) this .seeReferences[i];
0132: // if binding is valid then look at arguments
0133: if (messageSend.binding != null
0134: && messageSend.binding.isValidBinding()) {
0135: if (messageSend.arguments != null) {
0136: for (int j = 0, l = messageSend.arguments.length; j < l; j++) {
0137: if (messageSend.arguments[j].sourceStart == start) {
0138: return messageSend.arguments[j];
0139: }
0140: }
0141: }
0142: }
0143: }
0144: }
0145: }
0146: return null;
0147: }
0148:
0149: /*
0150: * @see org.eclipse.jdt.internal.compiler.ast.ASTNode#print(int, java.lang.StringBuffer)
0151: */
0152: public StringBuffer print(int indent, StringBuffer output) {
0153: printIndent(indent, output).append("/**\n"); //$NON-NLS-1$
0154: if (this .paramReferences != null) {
0155: for (int i = 0, length = this .paramReferences.length; i < length; i++) {
0156: printIndent(indent + 1, output).append(" * @param "); //$NON-NLS-1$
0157: this .paramReferences[i].print(indent, output).append(
0158: '\n');
0159: }
0160: }
0161: if (this .paramTypeParameters != null) {
0162: for (int i = 0, length = this .paramTypeParameters.length; i < length; i++) {
0163: printIndent(indent + 1, output).append(" * @param <"); //$NON-NLS-1$
0164: this .paramTypeParameters[i].print(indent, output)
0165: .append(">\n"); //$NON-NLS-1$
0166: }
0167: }
0168: if (this .returnStatement != null) {
0169: printIndent(indent + 1, output).append(" * @"); //$NON-NLS-1$
0170: this .returnStatement.print(indent, output).append('\n');
0171: }
0172: if (this .exceptionReferences != null) {
0173: for (int i = 0, length = this .exceptionReferences.length; i < length; i++) {
0174: printIndent(indent + 1, output).append(" * @throws "); //$NON-NLS-1$
0175: this .exceptionReferences[i].print(indent, output)
0176: .append('\n');
0177: }
0178: }
0179: if (this .seeReferences != null) {
0180: for (int i = 0, length = this .seeReferences.length; i < length; i++) {
0181: printIndent(indent + 1, output).append(" * @see "); //$NON-NLS-1$
0182: this .seeReferences[i].print(indent, output)
0183: .append('\n');
0184: }
0185: }
0186: printIndent(indent, output).append(" */\n"); //$NON-NLS-1$
0187: return output;
0188: }
0189:
0190: /*
0191: * Resolve type javadoc
0192: */
0193: public void resolve(ClassScope scope) {
0194:
0195: // @param tags
0196: int paramTagsSize = this .paramReferences == null ? 0
0197: : this .paramReferences.length;
0198: for (int i = 0; i < paramTagsSize; i++) {
0199: JavadocSingleNameReference param = this .paramReferences[i];
0200: scope.problemReporter().javadocUnexpectedTag(
0201: param.tagSourceStart, param.tagSourceEnd);
0202: }
0203: resolveTypeParameterTags(scope, true);
0204:
0205: // @return tags
0206: if (this .returnStatement != null) {
0207: scope.problemReporter().javadocUnexpectedTag(
0208: this .returnStatement.sourceStart,
0209: this .returnStatement.sourceEnd);
0210: }
0211:
0212: // @throws/@exception tags
0213: int throwsTagsLength = this .exceptionReferences == null ? 0
0214: : this .exceptionReferences.length;
0215: for (int i = 0; i < throwsTagsLength; i++) {
0216: TypeReference typeRef = this .exceptionReferences[i];
0217: int start, end;
0218: if (typeRef instanceof JavadocSingleTypeReference) {
0219: JavadocSingleTypeReference singleRef = (JavadocSingleTypeReference) typeRef;
0220: start = singleRef.tagSourceStart;
0221: end = singleRef.tagSourceEnd;
0222: } else if (typeRef instanceof JavadocQualifiedTypeReference) {
0223: JavadocQualifiedTypeReference qualifiedRef = (JavadocQualifiedTypeReference) typeRef;
0224: start = qualifiedRef.tagSourceStart;
0225: end = qualifiedRef.tagSourceEnd;
0226: } else {
0227: start = typeRef.sourceStart;
0228: end = typeRef.sourceEnd;
0229: }
0230: scope.problemReporter().javadocUnexpectedTag(start, end);
0231: }
0232:
0233: // @see tags
0234: int seeTagsLength = this .seeReferences == null ? 0
0235: : this .seeReferences.length;
0236: for (int i = 0; i < seeTagsLength; i++) {
0237: resolveReference(this .seeReferences[i], scope);
0238: }
0239:
0240: // @value tag
0241: boolean source15 = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5;
0242: if (!source15 && this .valuePositions != -1) {
0243: scope.problemReporter().javadocUnexpectedTag(
0244: (int) (this .valuePositions >>> 32),
0245: (int) this .valuePositions);
0246: }
0247: }
0248:
0249: /*
0250: * Resolve compilation unit javadoc
0251: */
0252: public void resolve(CompilationUnitScope unitScope) {
0253: // do nothing
0254: }
0255:
0256: /*
0257: * Resolve method javadoc
0258: */
0259: public void resolve(MethodScope methScope) {
0260:
0261: // get method declaration
0262: AbstractMethodDeclaration methDecl = methScope
0263: .referenceMethod();
0264: boolean overriding = methDecl == null /* field declaration */
0265: || methDecl.binding == null /* compiler error */
0266: ? false
0267: : !methDecl.binding.isStatic()
0268: && ((methDecl.binding.modifiers & (ExtraCompilerModifiers.AccImplementing | ExtraCompilerModifiers.AccOverriding)) != 0);
0269:
0270: // @see tags
0271: int seeTagsLength = this .seeReferences == null ? 0
0272: : this .seeReferences.length;
0273: boolean super Ref = false;
0274: for (int i = 0; i < seeTagsLength; i++) {
0275:
0276: // Resolve reference
0277: resolveReference(this .seeReferences[i], methScope);
0278:
0279: // see whether we can have a super reference
0280: if (methDecl != null
0281: && (methDecl.isConstructor() || overriding)
0282: && !super Ref) {
0283: if (this .seeReferences[i] instanceof JavadocMessageSend) {
0284: JavadocMessageSend messageSend = (JavadocMessageSend) this .seeReferences[i];
0285: // if binding is valid then look if we have a reference to an overriden method/constructor
0286: if (messageSend.binding != null
0287: && messageSend.binding.isValidBinding()
0288: && messageSend.actualReceiverType instanceof ReferenceBinding) {
0289: ReferenceBinding methodReceiverType = (ReferenceBinding) messageSend.actualReceiverType;
0290: if ((methodReceiverType
0291: .isSuperclassOf(methDecl.binding.declaringClass) || (methodReceiverType
0292: .isInterface() && methDecl.binding.declaringClass
0293: .implements Interface(
0294: methodReceiverType, true)))
0295: && CharOperation.equals(
0296: messageSend.selector,
0297: methDecl.selector)
0298: && (methDecl.binding.returnType
0299: .isCompatibleWith(messageSend.binding.returnType))) {
0300: if (messageSend.arguments == null
0301: && methDecl.arguments == null) {
0302: super Ref = true;
0303: } else if (messageSend.arguments != null
0304: && methDecl.arguments != null) {
0305: super Ref = methDecl.binding
0306: .areParameterErasuresEqual(messageSend.binding);
0307: }
0308: }
0309: }
0310: } else if (this .seeReferences[i] instanceof JavadocAllocationExpression) {
0311: JavadocAllocationExpression allocationExpr = (JavadocAllocationExpression) this .seeReferences[i];
0312: // if binding is valid then look if we have a reference to an overriden method/constructor
0313: if (allocationExpr.binding != null
0314: && allocationExpr.binding.isValidBinding()) {
0315: if (methDecl.binding.declaringClass
0316: .isCompatibleWith(allocationExpr.resolvedType)) {
0317: if (allocationExpr.arguments == null
0318: && methDecl.arguments == null) {
0319: super Ref = true;
0320: } else if (allocationExpr.arguments != null
0321: && methDecl.arguments != null
0322: && allocationExpr.arguments.length == methDecl.arguments.length) {
0323: super Ref = methDecl.binding
0324: .areParametersCompatibleWith(allocationExpr.binding.parameters);
0325: }
0326: }
0327: }
0328: }
0329: }
0330: }
0331:
0332: // Look at @Override annotations
0333: if (!super Ref && methDecl != null
0334: && methDecl.annotations != null) {
0335: int length = methDecl.annotations.length;
0336: for (int i = 0; i < length && !super Ref; i++) {
0337: super Ref = (methDecl.binding.tagBits & TagBits.AnnotationOverride) != 0;
0338: }
0339: }
0340:
0341: // Store if a reference exists to an overriden method/constructor or the method is in a local type,
0342: boolean reportMissing = methDecl == null
0343: || !((overriding && this .inheritedPositions != -1)
0344: || super Ref || (methDecl.binding.declaringClass != null && methDecl.binding.declaringClass
0345: .isLocalType()));
0346: if (!overriding && this .inheritedPositions != -1) {
0347: int start = (int) (this .inheritedPositions >>> 32);
0348: int end = (int) this .inheritedPositions;
0349: methScope.problemReporter()
0350: .javadocUnexpectedTag(start, end);
0351: }
0352:
0353: // @param tags
0354: CompilerOptions compilerOptions = methScope.compilerOptions();
0355: resolveParamTags(
0356: methScope,
0357: reportMissing,
0358: compilerOptions.reportUnusedParameterIncludeDocCommentReference /* considerParamRefAsUsage*/);
0359: resolveTypeParameterTags(methScope, reportMissing);
0360:
0361: // @return tags
0362: if (this .returnStatement == null) {
0363: if (reportMissing && methDecl != null) {
0364: if (methDecl.isMethod()) {
0365: MethodDeclaration meth = (MethodDeclaration) methDecl;
0366: if (meth.binding.returnType != TypeBinding.VOID) {
0367: // method with return should have @return tag
0368: methScope.problemReporter()
0369: .javadocMissingReturnTag(
0370: meth.returnType.sourceStart,
0371: meth.returnType.sourceEnd,
0372: methDecl.binding.modifiers);
0373: }
0374: }
0375: }
0376: } else {
0377: this .returnStatement.resolve(methScope);
0378: }
0379:
0380: // @throws/@exception tags
0381: resolveThrowsTags(methScope, reportMissing);
0382:
0383: // @value tag
0384: boolean source15 = compilerOptions.sourceLevel >= ClassFileConstants.JDK1_5;
0385: if (!source15 && methDecl != null && this .valuePositions != -1) {
0386: methScope.problemReporter().javadocUnexpectedTag(
0387: (int) (this .valuePositions >>> 32),
0388: (int) this .valuePositions);
0389: }
0390:
0391: // Resolve param tags with invalid syntax
0392: int length = this .invalidParameters == null ? 0
0393: : this .invalidParameters.length;
0394: for (int i = 0; i < length; i++) {
0395: this .invalidParameters[i].resolve(methScope, false, false);
0396: }
0397: }
0398:
0399: private void resolveReference(Expression reference, Scope scope) {
0400:
0401: // Perform resolve
0402: int problemCount = scope.referenceContext().compilationResult().problemCount;
0403: switch (scope.kind) {
0404: case Scope.METHOD_SCOPE:
0405: reference.resolveType((MethodScope) scope);
0406: break;
0407: case Scope.CLASS_SCOPE:
0408: reference.resolveType((ClassScope) scope);
0409: break;
0410: }
0411: boolean hasProblems = scope.referenceContext()
0412: .compilationResult().problemCount > problemCount;
0413:
0414: // Verify field references
0415: boolean source15 = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5;
0416: int scopeModifiers = -1;
0417: if (reference instanceof JavadocFieldReference) {
0418: JavadocFieldReference fieldRef = (JavadocFieldReference) reference;
0419:
0420: // Verify if this is a method reference
0421: // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51911
0422: if (fieldRef.methodBinding != null) {
0423: // cannot refer to method for @value tag
0424: if (fieldRef.tagValue == JavadocTagConstants.TAG_VALUE_VALUE) {
0425: if (scopeModifiers == -1)
0426: scopeModifiers = scope
0427: .getDeclarationModifiers();
0428: scope.problemReporter()
0429: .javadocInvalidValueReference(
0430: fieldRef.sourceStart,
0431: fieldRef.sourceEnd, scopeModifiers);
0432: } else if (fieldRef.receiverType != null) {
0433: if (scope.enclosingSourceType().isCompatibleWith(
0434: fieldRef.receiverType)) {
0435: fieldRef.bits |= ASTNode.SuperAccess;
0436: }
0437: fieldRef.methodBinding = scope.findMethod(
0438: (ReferenceBinding) fieldRef.receiverType,
0439: fieldRef.token, new TypeBinding[0],
0440: fieldRef);
0441: }
0442: }
0443:
0444: // Verify whether field ref should be static or not (for @value tags)
0445: else if (source15 && fieldRef.binding != null
0446: && fieldRef.binding.isValidBinding()) {
0447: if (fieldRef.tagValue == JavadocTagConstants.TAG_VALUE_VALUE
0448: && !fieldRef.binding.isStatic()) {
0449: if (scopeModifiers == -1)
0450: scopeModifiers = scope
0451: .getDeclarationModifiers();
0452: scope.problemReporter()
0453: .javadocInvalidValueReference(
0454: fieldRef.sourceStart,
0455: fieldRef.sourceEnd, scopeModifiers);
0456: }
0457: }
0458:
0459: // Verify type references
0460: if (!hasProblems
0461: && fieldRef.binding != null
0462: && fieldRef.binding.isValidBinding()
0463: && fieldRef.receiverType instanceof ReferenceBinding) {
0464: ReferenceBinding resolvedType = (ReferenceBinding) fieldRef.receiverType;
0465: verifyTypeReference(fieldRef, fieldRef.receiver, scope,
0466: source15, resolvedType,
0467: fieldRef.binding.modifiers);
0468: }
0469:
0470: // That's it for field references
0471: return;
0472: }
0473:
0474: // Verify type references
0475: if (!hasProblems
0476: && (reference instanceof JavadocSingleTypeReference || reference instanceof JavadocQualifiedTypeReference)
0477: && reference.resolvedType instanceof ReferenceBinding) {
0478: ReferenceBinding resolvedType = (ReferenceBinding) reference.resolvedType;
0479: verifyTypeReference(reference, reference, scope, source15,
0480: resolvedType, resolvedType.modifiers);
0481: }
0482:
0483: // Verify that message reference are not used for @value tags
0484: if (reference instanceof JavadocMessageSend) {
0485: JavadocMessageSend msgSend = (JavadocMessageSend) reference;
0486:
0487: // tag value
0488: if (source15
0489: && msgSend.tagValue == JavadocTagConstants.TAG_VALUE_VALUE) { // cannot refer to method for @value tag
0490: if (scopeModifiers == -1)
0491: scopeModifiers = scope.getDeclarationModifiers();
0492: scope.problemReporter().javadocInvalidValueReference(
0493: msgSend.sourceStart, msgSend.sourceEnd,
0494: scopeModifiers);
0495: }
0496:
0497: // Verify type references
0498: if (!hasProblems
0499: && msgSend.binding != null
0500: && msgSend.binding.isValidBinding()
0501: && msgSend.actualReceiverType instanceof ReferenceBinding) {
0502: ReferenceBinding resolvedType = (ReferenceBinding) msgSend.actualReceiverType;
0503: verifyTypeReference(msgSend, msgSend.receiver, scope,
0504: source15, resolvedType,
0505: msgSend.binding.modifiers);
0506: }
0507: }
0508:
0509: // Verify that constructor reference are not used for @value tags
0510: else if (reference instanceof JavadocAllocationExpression) {
0511: JavadocAllocationExpression alloc = (JavadocAllocationExpression) reference;
0512:
0513: // tag value
0514: if (source15
0515: && alloc.tagValue == JavadocTagConstants.TAG_VALUE_VALUE) { // cannot refer to method for @value tag
0516: if (scopeModifiers == -1)
0517: scopeModifiers = scope.getDeclarationModifiers();
0518: scope.problemReporter().javadocInvalidValueReference(
0519: alloc.sourceStart, alloc.sourceEnd,
0520: scopeModifiers);
0521: }
0522:
0523: // Verify type references
0524: if (!hasProblems && alloc.binding != null
0525: && alloc.binding.isValidBinding()
0526: && alloc.resolvedType instanceof ReferenceBinding) {
0527: ReferenceBinding resolvedType = (ReferenceBinding) alloc.resolvedType;
0528: verifyTypeReference(alloc, alloc.type, scope, source15,
0529: resolvedType, alloc.binding.modifiers);
0530: }
0531: }
0532:
0533: // Verify that there's no type variable reference
0534: // (javadoc does not accept them and this is not a referenced bug or requested enhancement)
0535: if (reference.resolvedType != null
0536: && reference.resolvedType.isTypeVariable()) {
0537: scope.problemReporter().javadocInvalidReference(
0538: reference.sourceStart, reference.sourceEnd);
0539: }
0540: }
0541:
0542: /*
0543: * Resolve @param tags while method scope
0544: */
0545: private void resolveParamTags(MethodScope scope,
0546: boolean reportMissing, boolean considerParamRefAsUsage) {
0547: AbstractMethodDeclaration methodDecl = scope.referenceMethod();
0548: int paramTagsSize = this .paramReferences == null ? 0
0549: : this .paramReferences.length;
0550:
0551: // If no referenced method (field initializer for example) then report a problem for each param tag
0552: if (methodDecl == null) {
0553: for (int i = 0; i < paramTagsSize; i++) {
0554: JavadocSingleNameReference param = this .paramReferences[i];
0555: scope.problemReporter().javadocUnexpectedTag(
0556: param.tagSourceStart, param.tagSourceEnd);
0557: }
0558: return;
0559: }
0560:
0561: // If no param tags then report a problem for each method argument
0562: int argumentsSize = methodDecl.arguments == null ? 0
0563: : methodDecl.arguments.length;
0564: if (paramTagsSize == 0) {
0565: if (reportMissing) {
0566: for (int i = 0; i < argumentsSize; i++) {
0567: Argument arg = methodDecl.arguments[i];
0568: scope.problemReporter().javadocMissingParamTag(
0569: arg.name, arg.sourceStart, arg.sourceEnd,
0570: methodDecl.binding.modifiers);
0571: }
0572: }
0573: } else {
0574: LocalVariableBinding[] bindings = new LocalVariableBinding[paramTagsSize];
0575: int maxBindings = 0;
0576:
0577: // Scan all @param tags
0578: for (int i = 0; i < paramTagsSize; i++) {
0579: JavadocSingleNameReference param = this .paramReferences[i];
0580: param.resolve(scope, true, considerParamRefAsUsage);
0581: if (param.binding != null
0582: && param.binding.isValidBinding()) {
0583: // Verify duplicated tags
0584: boolean found = false;
0585: for (int j = 0; j < maxBindings && !found; j++) {
0586: if (bindings[j] == param.binding) {
0587: scope
0588: .problemReporter()
0589: .javadocDuplicatedParamTag(
0590: param.token,
0591: param.sourceStart,
0592: param.sourceEnd,
0593: methodDecl.binding.modifiers);
0594: found = true;
0595: }
0596: }
0597: if (!found) {
0598: bindings[maxBindings++] = (LocalVariableBinding) param.binding;
0599: }
0600: }
0601: }
0602:
0603: // Look for undocumented arguments
0604: if (reportMissing) {
0605: for (int i = 0; i < argumentsSize; i++) {
0606: Argument arg = methodDecl.arguments[i];
0607: boolean found = false;
0608: for (int j = 0; j < maxBindings && !found; j++) {
0609: LocalVariableBinding binding = bindings[j];
0610: if (arg.binding == binding) {
0611: found = true;
0612: }
0613: }
0614: if (!found) {
0615: scope.problemReporter().javadocMissingParamTag(
0616: arg.name, arg.sourceStart,
0617: arg.sourceEnd,
0618: methodDecl.binding.modifiers);
0619: }
0620: }
0621: }
0622: }
0623: }
0624:
0625: /*
0626: * Resolve @param tags for type parameters
0627: */
0628: private void resolveTypeParameterTags(Scope scope,
0629: boolean reportMissing) {
0630: int paramTypeParamLength = this .paramTypeParameters == null ? 0
0631: : this .paramTypeParameters.length;
0632:
0633: // Get declaration infos
0634: TypeParameter[] parameters = null;
0635: TypeVariableBinding[] typeVariables = null;
0636: int modifiers = -1;
0637: switch (scope.kind) {
0638: case Scope.METHOD_SCOPE:
0639: AbstractMethodDeclaration methodDeclaration = ((MethodScope) scope)
0640: .referenceMethod();
0641: // If no referenced method (field initializer for example) then report a problem for each param tag
0642: if (methodDeclaration == null) {
0643: for (int i = 0; i < paramTypeParamLength; i++) {
0644: JavadocSingleNameReference param = this .paramReferences[i];
0645: scope.problemReporter().javadocUnexpectedTag(
0646: param.tagSourceStart, param.tagSourceEnd);
0647: }
0648: return;
0649: }
0650: parameters = methodDeclaration.typeParameters();
0651: typeVariables = methodDeclaration.binding.typeVariables;
0652: modifiers = methodDeclaration.binding.modifiers;
0653: break;
0654: case Scope.CLASS_SCOPE:
0655: TypeDeclaration typeDeclaration = ((ClassScope) scope).referenceContext;
0656: parameters = typeDeclaration.typeParameters;
0657: typeVariables = typeDeclaration.binding.typeVariables;
0658: modifiers = typeDeclaration.binding.modifiers;
0659: break;
0660: }
0661:
0662: // If no type variables then report a problem for each param type parameter tag
0663: if (typeVariables == null || typeVariables.length == 0) {
0664: for (int i = 0; i < paramTypeParamLength; i++) {
0665: JavadocSingleTypeReference param = this .paramTypeParameters[i];
0666: scope.problemReporter().javadocUnexpectedTag(
0667: param.tagSourceStart, param.tagSourceEnd);
0668: }
0669: return;
0670: }
0671:
0672: // If no param tags then report a problem for each declaration type parameter
0673: if (parameters != null) {
0674: int typeParametersLength = parameters.length;
0675: if (paramTypeParamLength == 0) {
0676: if (reportMissing) {
0677: for (int i = 0, l = typeParametersLength; i < l; i++) {
0678: scope.problemReporter().javadocMissingParamTag(
0679: parameters[i].name,
0680: parameters[i].sourceStart,
0681: parameters[i].sourceEnd, modifiers);
0682: }
0683: }
0684:
0685: // Otherwise verify that all param tags match type parameters
0686: } else if (typeVariables.length == typeParametersLength) {
0687: TypeVariableBinding[] bindings = new TypeVariableBinding[paramTypeParamLength];
0688:
0689: // Scan all @param tags
0690: for (int i = 0; i < paramTypeParamLength; i++) {
0691: JavadocSingleTypeReference param = this .paramTypeParameters[i];
0692: TypeBinding paramBindind = param
0693: .internalResolveType(scope);
0694: if (paramBindind != null
0695: && paramBindind.isValidBinding()) {
0696: if (paramBindind.isTypeVariable()) {
0697: // Verify duplicated tags
0698: boolean duplicate = false;
0699: for (int j = 0; j < i && !duplicate; j++) {
0700: if (bindings[j] == param.resolvedType) {
0701: scope.problemReporter()
0702: .javadocDuplicatedParamTag(
0703: param.token,
0704: param.sourceStart,
0705: param.sourceEnd,
0706: modifiers);
0707: duplicate = true;
0708: }
0709: }
0710: if (!duplicate) {
0711: bindings[i] = (TypeVariableBinding) param.resolvedType;
0712: }
0713: } else {
0714: scope.problemReporter()
0715: .javadocUndeclaredParamTagName(
0716: param.token,
0717: param.sourceStart,
0718: param.sourceEnd, modifiers);
0719: }
0720: }
0721: }
0722:
0723: // Look for undocumented type parameters
0724: for (int i = 0; i < typeParametersLength; i++) {
0725: TypeParameter parameter = parameters[i];
0726: boolean found = false;
0727: for (int j = 0; j < paramTypeParamLength && !found; j++) {
0728: if (parameter.binding == bindings[j]) {
0729: found = true;
0730: bindings[j] = null;
0731: }
0732: }
0733: if (!found && reportMissing) {
0734: scope.problemReporter().javadocMissingParamTag(
0735: parameter.name, parameter.sourceStart,
0736: parameter.sourceEnd, modifiers);
0737: }
0738: }
0739:
0740: // Report invalid param
0741: for (int i = 0; i < paramTypeParamLength; i++) {
0742: if (bindings[i] != null) {
0743: JavadocSingleTypeReference param = this .paramTypeParameters[i];
0744: scope.problemReporter()
0745: .javadocUndeclaredParamTagName(
0746: param.token, param.sourceStart,
0747: param.sourceEnd, modifiers);
0748: }
0749: }
0750: }
0751: }
0752: }
0753:
0754: /*
0755: * Resolve @throws/@exception tags while method scope
0756: */
0757: private void resolveThrowsTags(MethodScope methScope,
0758: boolean reportMissing) {
0759: AbstractMethodDeclaration md = methScope.referenceMethod();
0760: int throwsTagsLength = this .exceptionReferences == null ? 0
0761: : this .exceptionReferences.length;
0762:
0763: // If no referenced method (field initializer for example) then report a problem for each throws tag
0764: if (md == null) {
0765: for (int i = 0; i < throwsTagsLength; i++) {
0766: TypeReference typeRef = this .exceptionReferences[i];
0767: int start = typeRef.sourceStart;
0768: int end = typeRef.sourceEnd;
0769: if (typeRef instanceof JavadocQualifiedTypeReference) {
0770: start = ((JavadocQualifiedTypeReference) typeRef).tagSourceStart;
0771: end = ((JavadocQualifiedTypeReference) typeRef).tagSourceEnd;
0772: } else if (typeRef instanceof JavadocSingleTypeReference) {
0773: start = ((JavadocSingleTypeReference) typeRef).tagSourceStart;
0774: end = ((JavadocSingleTypeReference) typeRef).tagSourceEnd;
0775: }
0776: methScope.problemReporter().javadocUnexpectedTag(start,
0777: end);
0778: }
0779: return;
0780: }
0781:
0782: // If no throws tags then report a problem for each method thrown exception
0783: int boundExceptionLength = (md.binding == null) ? 0
0784: : md.binding.thrownExceptions.length;
0785: int thrownExceptionLength = md.thrownExceptions == null ? 0
0786: : md.thrownExceptions.length;
0787: if (throwsTagsLength == 0) {
0788: if (reportMissing) {
0789: for (int i = 0; i < boundExceptionLength; i++) {
0790: ReferenceBinding exceptionBinding = md.binding.thrownExceptions[i];
0791: if (exceptionBinding != null
0792: && exceptionBinding.isValidBinding()) { // flag only valid class name
0793: int j = i;
0794: while (j < thrownExceptionLength
0795: && exceptionBinding != md.thrownExceptions[j].resolvedType)
0796: j++;
0797: if (j < thrownExceptionLength) {
0798: methScope.problemReporter()
0799: .javadocMissingThrowsTag(
0800: md.thrownExceptions[j],
0801: md.binding.modifiers);
0802: }
0803: }
0804: }
0805: }
0806: } else {
0807: int maxRef = 0;
0808: TypeReference[] typeReferences = new TypeReference[throwsTagsLength];
0809:
0810: // Scan all @throws tags
0811: for (int i = 0; i < throwsTagsLength; i++) {
0812: TypeReference typeRef = this .exceptionReferences[i];
0813: typeRef.resolve(methScope);
0814: TypeBinding typeBinding = typeRef.resolvedType;
0815:
0816: if (typeBinding != null && typeBinding.isValidBinding()
0817: && typeBinding.isClass()) {
0818: // accept only valid class binding
0819: typeReferences[maxRef++] = typeRef;
0820: }
0821: }
0822:
0823: // Look for undocumented thrown exception
0824: for (int i = 0; i < boundExceptionLength; i++) {
0825: ReferenceBinding exceptionBinding = md.binding.thrownExceptions[i];
0826: if (exceptionBinding != null)
0827: exceptionBinding = (ReferenceBinding) exceptionBinding
0828: .erasure();
0829: boolean found = false;
0830: for (int j = 0; j < maxRef && !found; j++) {
0831: if (typeReferences[j] != null) {
0832: TypeBinding typeBinding = typeReferences[j].resolvedType;
0833: if (exceptionBinding == typeBinding) {
0834: found = true;
0835: typeReferences[j] = null;
0836: }
0837: }
0838: }
0839: if (!found && reportMissing) {
0840: if (exceptionBinding != null
0841: && exceptionBinding.isValidBinding()) { // flag only valid class name
0842: int k = i;
0843: while (k < thrownExceptionLength
0844: && exceptionBinding != md.thrownExceptions[k].resolvedType)
0845: k++;
0846: if (k < thrownExceptionLength) {
0847: methScope.problemReporter()
0848: .javadocMissingThrowsTag(
0849: md.thrownExceptions[k],
0850: md.binding.modifiers);
0851: }
0852: }
0853: }
0854: }
0855:
0856: // Verify additional @throws tags
0857: for (int i = 0; i < maxRef; i++) {
0858: TypeReference typeRef = typeReferences[i];
0859: if (typeRef != null) {
0860: boolean compatible = false;
0861: // thrown exceptions subclasses are accepted
0862: for (int j = 0; j < thrownExceptionLength
0863: && !compatible; j++) {
0864: TypeBinding exceptionBinding = md.thrownExceptions[j].resolvedType;
0865: if (exceptionBinding != null) {
0866: compatible = typeRef.resolvedType
0867: .isCompatibleWith(exceptionBinding);
0868: }
0869: }
0870:
0871: // If not compatible only complain on unchecked exception
0872: if (!compatible
0873: && !typeRef.resolvedType
0874: .isUncheckedException(false)) {
0875: methScope.problemReporter()
0876: .javadocInvalidThrowsClassName(typeRef,
0877: md.binding.modifiers);
0878: }
0879: }
0880: }
0881: }
0882: }
0883:
0884: private void verifyTypeReference(Expression reference,
0885: Expression typeReference, Scope scope, boolean source15,
0886: ReferenceBinding resolvedType, int modifiers) {
0887: if (resolvedType.isValidBinding()) {
0888: int scopeModifiers = -1;
0889:
0890: // reference must have enough visibility to be used
0891: if (!canBeSeen(
0892: scope.problemReporter().options.reportInvalidJavadocTagsVisibility,
0893: modifiers)) {
0894: scope.problemReporter().javadocHiddenReference(
0895: typeReference.sourceStart, reference.sourceEnd,
0896: scope, modifiers);
0897: return;
0898: }
0899:
0900: // type reference must have enough visibility to be used
0901: if (reference != typeReference) {
0902: if (!canBeSeen(
0903: scope.problemReporter().options.reportInvalidJavadocTagsVisibility,
0904: resolvedType.modifiers)) {
0905: scope.problemReporter().javadocHiddenReference(
0906: typeReference.sourceStart,
0907: typeReference.sourceEnd, scope,
0908: resolvedType.modifiers);
0909: return;
0910: }
0911: }
0912:
0913: // member types
0914: if (resolvedType.isMemberType()) {
0915: ReferenceBinding topLevelType = resolvedType;
0916: // rebuild and store (in reverse order) compound name to handle embedded inner class
0917: int packageLength = topLevelType.fPackage.compoundName.length;
0918: int depth = resolvedType.depth();
0919: int idx = depth + packageLength;
0920: char[][] computedCompoundName = new char[idx + 1][];
0921: computedCompoundName[idx] = topLevelType.sourceName;
0922: while (topLevelType.enclosingType() != null) {
0923: topLevelType = topLevelType.enclosingType();
0924: computedCompoundName[--idx] = topLevelType.sourceName;
0925: }
0926:
0927: // add package information
0928: for (int i = packageLength; --i >= 0;) {
0929: computedCompoundName[--idx] = topLevelType.fPackage.compoundName[i];
0930: }
0931:
0932: ClassScope topLevelScope = scope.classScope();
0933: // when scope is not on compilation unit type, then inner class may not be visible...
0934: if (topLevelScope.parent.kind != Scope.COMPILATION_UNIT_SCOPE
0935: || !CharOperation.equals(
0936: topLevelType.sourceName,
0937: topLevelScope.referenceContext.name)) {
0938: topLevelScope = topLevelScope.outerMostClassScope();
0939: if (typeReference instanceof JavadocSingleTypeReference) {
0940: // inner class single reference can only be done in same unit
0941: if ((!source15 && depth == 1)
0942: || topLevelType != topLevelScope.referenceContext.binding) {
0943: // search for corresponding import
0944: boolean hasValidImport = false;
0945: if (source15) {
0946: CompilationUnitScope unitScope = topLevelScope
0947: .compilationUnitScope();
0948: ImportBinding[] imports = unitScope.imports;
0949: int length = imports == null ? 0
0950: : imports.length;
0951: mainLoop: for (int i = 0; i < length; i++) {
0952: char[][] compoundName = imports[i].compoundName;
0953: int compoundNameLength = compoundName.length;
0954: if ((imports[i].onDemand && compoundNameLength == computedCompoundName.length - 1)
0955: || (compoundNameLength == computedCompoundName.length)) {
0956: for (int j = compoundNameLength; --j >= 0;) {
0957: if (CharOperation
0958: .equals(
0959: imports[i].compoundName[j],
0960: computedCompoundName[j])) {
0961: if (j == 0) {
0962: hasValidImport = true;
0963: break mainLoop;
0964: }
0965: } else {
0966: break;
0967: }
0968: }
0969: }
0970: }
0971: if (!hasValidImport) {
0972: if (scopeModifiers == -1)
0973: scopeModifiers = scope
0974: .getDeclarationModifiers();
0975: scope
0976: .problemReporter()
0977: .javadocInvalidMemberTypeQualification(
0978: typeReference.sourceStart,
0979: typeReference.sourceEnd,
0980: scopeModifiers);
0981: }
0982: } else {
0983: if (scopeModifiers == -1)
0984: scopeModifiers = scope
0985: .getDeclarationModifiers();
0986: scope
0987: .problemReporter()
0988: .javadocInvalidMemberTypeQualification(
0989: typeReference.sourceStart,
0990: typeReference.sourceEnd,
0991: scopeModifiers);
0992: return;
0993: }
0994: }
0995: }
0996: }
0997: }
0998: }
0999: }
1000:
1001: public void traverse(ASTVisitor visitor, BlockScope scope) {
1002: if (visitor.visit(this , scope)) {
1003: if (this .paramReferences != null) {
1004: for (int i = 0, length = this .paramReferences.length; i < length; i++) {
1005: this .paramReferences[i].traverse(visitor, scope);
1006: }
1007: }
1008: if (this .paramTypeParameters != null) {
1009: for (int i = 0, length = this .paramTypeParameters.length; i < length; i++) {
1010: this .paramTypeParameters[i]
1011: .traverse(visitor, scope);
1012: }
1013: }
1014: if (this .returnStatement != null) {
1015: this .returnStatement.traverse(visitor, scope);
1016: }
1017: if (this .exceptionReferences != null) {
1018: for (int i = 0, length = this .exceptionReferences.length; i < length; i++) {
1019: this .exceptionReferences[i]
1020: .traverse(visitor, scope);
1021: }
1022: }
1023: if (this .seeReferences != null) {
1024: for (int i = 0, length = this .seeReferences.length; i < length; i++) {
1025: this .seeReferences[i].traverse(visitor, scope);
1026: }
1027: }
1028: }
1029: visitor.endVisit(this , scope);
1030: }
1031:
1032: public void traverse(ASTVisitor visitor, ClassScope scope) {
1033: if (visitor.visit(this , scope)) {
1034: if (this .paramReferences != null) {
1035: for (int i = 0, length = this .paramReferences.length; i < length; i++) {
1036: this .paramReferences[i].traverse(visitor, scope);
1037: }
1038: }
1039: if (this .paramTypeParameters != null) {
1040: for (int i = 0, length = this .paramTypeParameters.length; i < length; i++) {
1041: this .paramTypeParameters[i]
1042: .traverse(visitor, scope);
1043: }
1044: }
1045: if (this .returnStatement != null) {
1046: this .returnStatement.traverse(visitor, scope);
1047: }
1048: if (this .exceptionReferences != null) {
1049: for (int i = 0, length = this .exceptionReferences.length; i < length; i++) {
1050: this .exceptionReferences[i]
1051: .traverse(visitor, scope);
1052: }
1053: }
1054: if (this .seeReferences != null) {
1055: for (int i = 0, length = this.seeReferences.length; i < length; i++) {
1056: this.seeReferences[i].traverse(visitor, scope);
1057: }
1058: }
1059: }
1060: visitor.endVisit(this, scope);
1061: }
1062: }
|