| /******************************************************************************* |
| * Copyright (c) 2000, 2019 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Stephan Herrmann - Contribution for |
| * Bug 400874 - [1.8][compiler] Inference infrastructure should evolve to meet JLS8 18.x (Part G of JSR335 spec) |
| * Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler.ast; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ASTVisitor; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.eclipse.jdt.internal.compiler.lookup.*; |
| import org.eclipse.jdt.internal.compiler.parser.JavadocTagConstants; |
| |
| /** |
| * Node representing a structured Javadoc comment |
| */ |
| public class Javadoc extends ASTNode { |
| |
| public JavadocSingleNameReference[] paramReferences; // @param |
| public JavadocSingleTypeReference[] paramTypeParameters; // @param |
| public TypeReference[] exceptionReferences; // @throws, @exception |
| public JavadocReturnStatement returnStatement; // @return |
| public Expression[] seeReferences; // @see |
| public IJavadocTypeReference[] usesReferences; // @uses |
| public IJavadocTypeReference[] providesReferences; // @provides |
| public long[] inheritedPositions = null; |
| // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51600 |
| // Store param references for tag with invalid syntax |
| public JavadocSingleNameReference[] invalidParameters; // @param |
| // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=153399 |
| // Store value tag positions |
| public long valuePositions = -1; |
| |
| public Javadoc(int sourceStart, int sourceEnd) { |
| this.sourceStart = sourceStart; |
| this.sourceEnd = sourceEnd; |
| this.bits |= ASTNode.ResolveJavadoc; |
| } |
| /** |
| * Returns whether a type can be seen at a given visibility level or not. |
| * |
| * @param visibility Level of visiblity allowed to see references |
| * @param modifiers modifiers of java element to be seen |
| * @return true if the type can be seen, false otherwise |
| */ |
| boolean canBeSeen(int visibility, int modifiers) { |
| if (modifiers < 0) return true; |
| switch (modifiers & ExtraCompilerModifiers.AccVisibilityMASK) { |
| case ClassFileConstants.AccPublic : |
| return true; |
| case ClassFileConstants.AccProtected: |
| return (visibility != ClassFileConstants.AccPublic); |
| case ClassFileConstants.AccDefault: |
| return (visibility == ClassFileConstants.AccDefault || visibility == ClassFileConstants.AccPrivate); |
| case ClassFileConstants.AccPrivate: |
| return (visibility == ClassFileConstants.AccPrivate); |
| } |
| return true; |
| } |
| |
| /* |
| * Search node with a given staring position in javadoc objects arrays. |
| */ |
| public ASTNode getNodeStartingAt(int start) { |
| int length = 0; |
| // parameters array |
| 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) { |
| 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.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.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.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, 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.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, l=messageSend.arguments.length; j<l; j++) { |
| if (messageSend.arguments[j].sourceStart == start) { |
| return messageSend.arguments[j]; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /* |
| * @see org.eclipse.jdt.internal.compiler.ast.ASTNode#print(int, java.lang.StringBuffer) |
| */ |
| @Override |
| public StringBuffer print(int indent, StringBuffer output) { |
| printIndent(indent, output).append("/**\n"); //$NON-NLS-1$ |
| 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.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(" * @"); //$NON-NLS-1$ |
| this.returnStatement.print(indent, output).append('\n'); |
| } |
| 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.exceptionReferences[i].print(indent, output).append('\n'); |
| } |
| } |
| 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.seeReferences[i].print(indent, output).append('\n'); |
| } |
| } |
| printIndent(indent, output).append(" */\n"); //$NON-NLS-1$ |
| return output; |
| } |
| |
| /* |
| * Resolve type javadoc |
| */ |
| public void resolve(ClassScope scope) { |
| if ((this.bits & ASTNode.ResolveJavadoc) == 0) { |
| return; |
| } |
| |
| this.bits &= ~ASTNode.ResolveJavadoc;// avoid double resolution |
| |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=247037, @inheritDoc tag cannot |
| // be used in the documentation comment for a class or interface. |
| if (this.inheritedPositions != null) { |
| int length = this.inheritedPositions.length; |
| for (int i = 0; i < length; ++i) { |
| int start = (int) (this.inheritedPositions[i] >>> 32); |
| int end = (int) this.inheritedPositions[i]; |
| scope.problemReporter().javadocUnexpectedTag(start, end); |
| } |
| } |
| // @param tags |
| int paramTagsSize = this.paramReferences == null ? 0 : this.paramReferences.length; |
| for (int i = 0; i < paramTagsSize; i++) { |
| if(scope.referenceContext instanceof RecordDeclaration) { |
| RecordDeclaration rd = (RecordDeclaration)scope.referenceContext; |
| if( rd.nRecordComponents > 0) |
| break; |
| } |
| JavadocSingleNameReference param = this.paramReferences[i]; |
| scope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd); |
| } |
| resolveTypeParameterTags(scope, true); |
| |
| // @return tags |
| if (this.returnStatement != null) { |
| scope.problemReporter().javadocUnexpectedTag(this.returnStatement.sourceStart, this.returnStatement.sourceEnd); |
| } |
| |
| // @throws/@exception tags |
| int throwsTagsLength = this.exceptionReferences == null ? 0 : this.exceptionReferences.length; |
| for (int i = 0; i < throwsTagsLength; i++) { |
| TypeReference typeRef = this.exceptionReferences[i]; |
| int start, end; |
| if (typeRef instanceof JavadocSingleTypeReference) { |
| JavadocSingleTypeReference singleRef = (JavadocSingleTypeReference) typeRef; |
| start = singleRef.tagSourceStart; |
| end = singleRef.tagSourceEnd; |
| } else if (typeRef instanceof JavadocQualifiedTypeReference) { |
| JavadocQualifiedTypeReference qualifiedRef = (JavadocQualifiedTypeReference) typeRef; |
| start = qualifiedRef.tagSourceStart; |
| end = qualifiedRef.tagSourceEnd; |
| } else { |
| start = typeRef.sourceStart; |
| end = typeRef.sourceEnd; |
| } |
| scope.problemReporter().javadocUnexpectedTag(start, end); |
| } |
| |
| // @see tags |
| int seeTagsLength = this.seeReferences == null ? 0 : this.seeReferences.length; |
| for (int i = 0; i < seeTagsLength; i++) { |
| resolveReference(this.seeReferences[i], scope); |
| } |
| |
| // @value tag |
| boolean source15 = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5; |
| if (!source15 && this.valuePositions != -1) { |
| scope.problemReporter().javadocUnexpectedTag((int)(this.valuePositions>>>32), (int) this.valuePositions); |
| } |
| } |
| |
| /* |
| * Resolve compilation unit javadoc |
| */ |
| public void resolve(CompilationUnitScope unitScope) { |
| if ((this.bits & ASTNode.ResolveJavadoc) == 0) { |
| return; |
| } |
| // Do nothing - This is to mimic the SDK's javadoc tool behavior, which neither |
| // sanity checks nor generates documentation using comments at the CU scope |
| // (unless the unit happens to be package-info.java - in which case we don't come here.) |
| } |
| |
| /* |
| * Resolve module info javadoc |
| */ |
| public void resolve(ModuleScope moduleScope) { |
| if ((this.bits & ASTNode.ResolveJavadoc) == 0) { |
| return; |
| } |
| |
| this.bits &= ~ASTNode.ResolveJavadoc;// avoid double resolution |
| |
| // @see tags |
| int seeTagsLength = this.seeReferences == null ? 0 : this.seeReferences.length; |
| for (int i = 0; i < seeTagsLength; i++) { |
| // Resolve reference |
| resolveReference(this.seeReferences[i], moduleScope); |
| } |
| |
| resolveUsesTags(moduleScope, true); |
| resolveProvidesTags(moduleScope, true); |
| } |
| |
| /* |
| * Resolve method javadoc |
| */ |
| public void resolve(MethodScope methScope) { |
| if ((this.bits & ASTNode.ResolveJavadoc) == 0) { |
| return; |
| } |
| |
| this.bits &= ~ASTNode.ResolveJavadoc;// avoid double resolution |
| |
| // get method declaration |
| AbstractMethodDeclaration methDecl = methScope.referenceMethod(); |
| boolean overriding = methDecl == null /* field declaration */ || methDecl.binding == null /* compiler error */ |
| ? false : |
| !methDecl.binding.isStatic() && ((methDecl.binding.modifiers & (ExtraCompilerModifiers.AccImplementing | ExtraCompilerModifiers.AccOverriding)) != 0); |
| |
| // @see tags |
| int seeTagsLength = this.seeReferences == null ? 0 : this.seeReferences.length; |
| boolean superRef = false; |
| for (int i = 0; i < seeTagsLength; i++) { |
| |
| // Resolve reference |
| resolveReference(this.seeReferences[i], methScope); |
| |
| // see whether we can have a super reference |
| if (methDecl != null && !superRef) { |
| if (!methDecl.isConstructor()) { |
| if (overriding && 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() && messageSend.actualReceiverType instanceof ReferenceBinding) { |
| ReferenceBinding methodReceiverType = (ReferenceBinding) messageSend.actualReceiverType; |
| TypeBinding superType = methDecl.binding.declaringClass.findSuperTypeOriginatingFrom(methodReceiverType); |
| if (superType != null && TypeBinding.notEquals(superType.original(), methDecl.binding.declaringClass) && CharOperation.equals(messageSend.selector, methDecl.selector)) { |
| if (methScope.environment().methodVerifier().doesMethodOverride(methDecl.binding, messageSend.binding.original())) { |
| superRef = true; |
| } |
| } |
| } |
| } |
| } 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()) { |
| ReferenceBinding allocType = (ReferenceBinding) allocationExpr.resolvedType.original(); |
| ReferenceBinding superType = (ReferenceBinding) methDecl.binding.declaringClass.findSuperTypeOriginatingFrom(allocType); |
| if (superType != null && TypeBinding.notEquals(superType.original(), methDecl.binding.declaringClass)) { |
| MethodBinding superConstructor = methScope.getConstructor(superType, methDecl.binding.parameters, allocationExpr); |
| if (superConstructor.isValidBinding() && superConstructor.original() == allocationExpr.binding.original()) { |
| MethodBinding current = methDecl.binding; |
| // work 'against' better inference in 1.8 (otherwise comparing (G<T> with G<Object>) would fail): |
| if (methScope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_8 |
| && current.typeVariables != Binding.NO_TYPE_VARIABLES) |
| { |
| current = current.asRawMethod(methScope.environment()); |
| } |
| if (superConstructor.areParametersEqual(current)) { |
| superRef = true; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // Look at @Override annotations |
| if (!superRef && methDecl != null && methDecl.annotations != null) { |
| int length = methDecl.annotations.length; |
| for (int i=0; i<length && !superRef; i++) { |
| superRef = (methDecl.binding.tagBits & TagBits.AnnotationOverride) != 0; |
| } |
| } |
| |
| // Store if a reference exists to an overriden method/constructor or the method is in a local type, |
| boolean reportMissing = methDecl == null || !((overriding && this.inheritedPositions != null) || superRef || (methDecl.binding.declaringClass != null && methDecl.binding.declaringClass.isLocalType())); |
| if (!overriding && this.inheritedPositions != null) { |
| int length = this.inheritedPositions.length; |
| for (int i = 0; i < length; ++i) { |
| int start = (int) (this.inheritedPositions[i] >>> 32); |
| int end = (int) this.inheritedPositions[i]; |
| methScope.problemReporter().javadocUnexpectedTag(start, end); |
| } |
| } |
| |
| // @param tags |
| CompilerOptions compilerOptions = methScope.compilerOptions(); |
| resolveParamTags(methScope, reportMissing, compilerOptions.reportUnusedParameterIncludeDocCommentReference /* considerParamRefAsUsage*/); |
| resolveTypeParameterTags(methScope, reportMissing && compilerOptions.reportMissingJavadocTagsMethodTypeParameters); |
| |
| // @return tags |
| if (this.returnStatement == null) { |
| if (reportMissing && methDecl != null) { |
| if (methDecl.isMethod()) { |
| MethodDeclaration meth = (MethodDeclaration) methDecl; |
| if (meth.binding.returnType != TypeBinding.VOID) { |
| // method with return should have @return tag |
| methScope.problemReporter().javadocMissingReturnTag(meth.returnType.sourceStart, meth.returnType.sourceEnd, methDecl.binding.modifiers); |
| } |
| } |
| } |
| } else { |
| this.returnStatement.resolve(methScope); |
| } |
| |
| // @throws/@exception tags |
| resolveThrowsTags(methScope, reportMissing); |
| |
| // @value tag |
| boolean source15 = compilerOptions.sourceLevel >= ClassFileConstants.JDK1_5; |
| if (!source15 && methDecl != null && this.valuePositions != -1) { |
| methScope.problemReporter().javadocUnexpectedTag((int)(this.valuePositions>>>32), (int) this.valuePositions); |
| } |
| |
| // Resolve param tags with invalid syntax |
| int length = this.invalidParameters == null ? 0 : this.invalidParameters.length; |
| for (int i = 0; i < length; i++) { |
| this.invalidParameters[i].resolve(methScope, false, false); |
| } |
| } |
| |
| private void resolveReference(Expression reference, Scope scope) { |
| |
| // Perform resolve |
| int problemCount = scope.referenceContext().compilationResult().problemCount; |
| switch (scope.kind) { |
| case Scope.METHOD_SCOPE: |
| reference.resolveType((MethodScope)scope); |
| break; |
| case Scope.CLASS_SCOPE: |
| reference.resolveType((ClassScope)scope); |
| break; |
| } |
| boolean hasProblems = scope.referenceContext().compilationResult().problemCount > problemCount; |
| |
| // Verify field references |
| boolean source15 = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5; |
| int scopeModifiers = -1; |
| if (reference instanceof JavadocFieldReference) { |
| JavadocFieldReference fieldRef = (JavadocFieldReference) reference; |
| |
| // 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 == JavadocTagConstants.TAG_VALUE_VALUE) { |
| if (scopeModifiers == -1) scopeModifiers = scope.getDeclarationModifiers(); |
| scope.problemReporter().javadocInvalidValueReference(fieldRef.sourceStart, fieldRef.sourceEnd, scopeModifiers); |
| } |
| else if (fieldRef.actualReceiverType != null) { |
| if (scope.kind != Scope.MODULE_SCOPE && scope.enclosingSourceType().isCompatibleWith(fieldRef.actualReceiverType)) { |
| fieldRef.bits |= ASTNode.SuperAccess; |
| } |
| ReferenceBinding resolvedType = (ReferenceBinding) fieldRef.actualReceiverType; |
| if (CharOperation.equals(resolvedType.sourceName(), fieldRef.token)) { |
| fieldRef.methodBinding = scope.getConstructor(resolvedType, Binding.NO_TYPES, fieldRef); |
| } else { |
| fieldRef.methodBinding = scope.findMethod(resolvedType, fieldRef.token, Binding.NO_TYPES, fieldRef, false); |
| } |
| } |
| } |
| |
| // Verify whether field ref should be static or not (for @value tags) |
| else if (source15 && fieldRef.binding != null && fieldRef.binding.isValidBinding()) { |
| if (fieldRef.tagValue == JavadocTagConstants.TAG_VALUE_VALUE && !fieldRef.binding.isStatic()) { |
| if (scopeModifiers == -1) scopeModifiers = scope.getDeclarationModifiers(); |
| scope.problemReporter().javadocInvalidValueReference(fieldRef.sourceStart, fieldRef.sourceEnd, scopeModifiers); |
| } |
| } |
| |
| // Verify type references |
| if (!hasProblems && fieldRef.binding != null && fieldRef.binding.isValidBinding() && fieldRef.actualReceiverType instanceof ReferenceBinding) { |
| ReferenceBinding resolvedType = (ReferenceBinding) fieldRef.actualReceiverType; |
| verifyTypeReference(fieldRef, fieldRef.receiver, scope, source15, resolvedType, fieldRef.binding.modifiers); |
| } |
| |
| // That's it for field references |
| return; |
| } |
| |
| // Verify type references |
| if (!hasProblems && (reference instanceof JavadocSingleTypeReference || reference instanceof JavadocQualifiedTypeReference) && reference.resolvedType instanceof ReferenceBinding) { |
| ReferenceBinding resolvedType = (ReferenceBinding) reference.resolvedType; |
| verifyTypeReference(reference, reference, scope, source15, resolvedType, resolvedType.modifiers); |
| } |
| |
| // Verify that message reference are not used for @value tags |
| if (reference instanceof JavadocMessageSend) { |
| JavadocMessageSend msgSend = (JavadocMessageSend) reference; |
| |
| // tag value |
| if (source15 && msgSend.tagValue == JavadocTagConstants.TAG_VALUE_VALUE) { // cannot refer to method for @value tag |
| if (scopeModifiers == -1) scopeModifiers = scope.getDeclarationModifiers(); |
| scope.problemReporter().javadocInvalidValueReference(msgSend.sourceStart, msgSend.sourceEnd, scopeModifiers); |
| } |
| |
| // Verify type references |
| if (!hasProblems && msgSend.binding != null && msgSend.binding.isValidBinding() && msgSend.actualReceiverType instanceof ReferenceBinding) { |
| ReferenceBinding resolvedType = (ReferenceBinding) msgSend.actualReceiverType; |
| verifyTypeReference(msgSend, msgSend.receiver, scope, source15, resolvedType, msgSend.binding.modifiers); |
| } |
| } |
| |
| // Verify that constructor reference are not used for @value tags |
| else if (reference instanceof JavadocAllocationExpression) { |
| JavadocAllocationExpression alloc = (JavadocAllocationExpression) reference; |
| |
| // tag value |
| if (source15 && alloc.tagValue == JavadocTagConstants.TAG_VALUE_VALUE) { // cannot refer to method for @value tag |
| if (scopeModifiers == -1) scopeModifiers = scope.getDeclarationModifiers(); |
| scope.problemReporter().javadocInvalidValueReference(alloc.sourceStart, alloc.sourceEnd, scopeModifiers); |
| } |
| |
| // Verify type references |
| if (!hasProblems && alloc.binding != null && alloc.binding.isValidBinding() && alloc.resolvedType instanceof ReferenceBinding) { |
| ReferenceBinding resolvedType = (ReferenceBinding) alloc.resolvedType; |
| verifyTypeReference(alloc, alloc.type, scope, source15, resolvedType, alloc.binding.modifiers); |
| } |
| } |
| |
| // Verify that there's no type variable reference |
| // (javadoc does not accept them and this is not a referenced bug or requested enhancement) |
| else if (reference instanceof JavadocSingleTypeReference && reference.resolvedType != null && reference.resolvedType.isTypeVariable()) { |
| scope.problemReporter().javadocInvalidReference(reference.sourceStart, reference.sourceEnd); |
| } |
| } |
| |
| /* |
| * Resolve @param tags while method scope |
| */ |
| private void resolveParamTags(MethodScope scope, boolean reportMissing, boolean considerParamRefAsUsage) { |
| AbstractMethodDeclaration methodDecl = scope.referenceMethod(); |
| 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 (methodDecl == null) { |
| for (int i = 0; i < paramTagsSize; i++) { |
| JavadocSingleNameReference param = this.paramReferences[i]; |
| scope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd); |
| } |
| return; |
| } |
| |
| // If no param tags then report a problem for each method argument |
| int argumentsSize = methodDecl.arguments == null ? 0 : methodDecl.arguments.length; |
| if (paramTagsSize == 0) { |
| if (reportMissing) { |
| for (int i = 0; i < argumentsSize; i++) { |
| Argument arg = methodDecl.arguments[i]; |
| scope.problemReporter().javadocMissingParamTag(arg.name, arg.sourceStart, arg.sourceEnd, methodDecl.binding.modifiers); |
| } |
| } |
| } else { |
| LocalVariableBinding[] bindings = new LocalVariableBinding[paramTagsSize]; |
| int maxBindings = 0; |
| |
| // Scan all @param tags |
| for (int i = 0; i < paramTagsSize; i++) { |
| JavadocSingleNameReference param = this.paramReferences[i]; |
| param.resolve(scope, true, considerParamRefAsUsage); |
| 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) { |
| scope.problemReporter().javadocDuplicatedParamTag(param.token, param.sourceStart, param.sourceEnd, methodDecl.binding.modifiers); |
| found = true; |
| } |
| } |
| if (!found) { |
| bindings[maxBindings++] = (LocalVariableBinding) param.binding; |
| } |
| } |
| } |
| |
| // Look for undocumented arguments |
| if (reportMissing) { |
| for (int i = 0; i < argumentsSize; i++) { |
| Argument arg = methodDecl.arguments[i]; |
| boolean found = false; |
| for (int j = 0; j < maxBindings && !found; j++) { |
| LocalVariableBinding binding = bindings[j]; |
| if (arg.binding == binding) { |
| found = true; |
| } |
| } |
| if (!found) { |
| scope.problemReporter().javadocMissingParamTag(arg.name, arg.sourceStart, arg.sourceEnd, methodDecl.binding.modifiers); |
| } |
| } |
| } |
| } |
| } |
| |
| /* |
| * Resolve @uses tags while block scope |
| */ |
| private void resolveUsesTags(BlockScope scope, boolean reportMissing) { |
| ModuleDeclaration moduleDecl = (ModuleDeclaration)scope.referenceContext(); |
| int usesTagsSize = this.usesReferences == null ? 0 : this.usesReferences.length; |
| |
| // If no referenced module then report a problem for each uses tag |
| if (moduleDecl == null) { |
| for (int i = 0; i < usesTagsSize; i++) { |
| IJavadocTypeReference uses = this.usesReferences[i]; |
| scope.problemReporter().javadocUnexpectedTag(uses.getTagSourceStart(), uses.getTagSourceEnd()); |
| } |
| return; |
| } |
| |
| // If no uses tags then report a problem for each uses reference |
| int usesSize = moduleDecl.usesCount; |
| if (usesTagsSize == 0) { |
| if (reportMissing) { |
| for (int i = 0; i < usesSize; i++) { |
| UsesStatement uses = moduleDecl.uses[i]; |
| scope.problemReporter().javadocMissingUsesTag(uses.serviceInterface, uses.sourceStart, uses.sourceEnd, moduleDecl.binding.modifiers); |
| } |
| } |
| } else { |
| TypeBinding[] bindings = new TypeBinding[usesTagsSize]; |
| int maxBindings = 0; |
| |
| // Scan all @uses tags |
| for (int i = 0; i < usesTagsSize; i++) { |
| TypeReference usesRef = (TypeReference)this.usesReferences[i]; |
| try { |
| usesRef.resolve(scope); |
| if (usesRef.resolvedType != null && usesRef.resolvedType.isValidBinding()) { |
| // Verify duplicated tags |
| boolean found = false; |
| for (int j = 0; j < maxBindings && !found; j++) { |
| if (bindings[j].equals(usesRef.resolvedType)) { |
| scope.problemReporter().javadocDuplicatedUsesTag(usesRef.sourceStart, usesRef.sourceEnd); |
| found = true; |
| } |
| } |
| if (!found) { |
| bindings[maxBindings++] = usesRef.resolvedType; |
| } |
| } |
| } catch (Exception e) { |
| scope.problemReporter().javadocInvalidUsesClass(usesRef.sourceStart, usesRef.sourceEnd); |
| } |
| } |
| |
| // Look for undocumented uses |
| if (reportMissing) { |
| for (int i = 0; i < usesSize; i++) { |
| UsesStatement uses = moduleDecl.uses[i]; |
| boolean found = false; |
| for (int j = 0; j < maxBindings && !found; j++) { |
| TypeBinding binding = bindings[j]; |
| if (uses.serviceInterface.getTypeBinding(scope).equals(binding)) { |
| found = true; |
| } |
| } |
| if (!found) { |
| scope.problemReporter().javadocMissingUsesTag(uses.serviceInterface, uses.sourceStart, uses.sourceEnd, moduleDecl.binding.modifiers); |
| } |
| } |
| } |
| } |
| } |
| |
| /* |
| * Resolve @provides tags while block scope |
| */ |
| private void resolveProvidesTags(BlockScope scope, boolean reportMissing) { |
| ModuleDeclaration moduleDecl = (ModuleDeclaration)scope.referenceContext(); |
| int providesTagsSize = this.providesReferences == null ? 0 : this.providesReferences.length; |
| |
| // If no referenced module then report a problem for each uses tag |
| if (moduleDecl == null) { |
| for (int i = 0; i < providesTagsSize; i++) { |
| IJavadocTypeReference provides = this.providesReferences[i]; |
| scope.problemReporter().javadocUnexpectedTag(provides.getTagSourceStart(), provides.getTagSourceEnd()); |
| } |
| return; |
| } |
| |
| // If no uses tags then report a problem for each uses reference |
| int providesSize = moduleDecl.servicesCount; |
| if (providesTagsSize == 0) { |
| if (reportMissing) { |
| for (int i = 0; i < providesSize; i++) { |
| ProvidesStatement provides = moduleDecl.services[i]; |
| scope.problemReporter().javadocMissingProvidesTag(provides.serviceInterface, provides.sourceStart, provides.sourceEnd, moduleDecl.binding.modifiers); |
| } |
| } |
| } else { |
| TypeBinding[] bindings = new TypeBinding[providesTagsSize]; |
| int maxBindings = 0; |
| |
| // Scan all @provides tags |
| for (int i = 0; i < providesTagsSize; i++) { |
| TypeReference providesRef = (TypeReference)this.providesReferences[i]; |
| try { |
| providesRef.resolve(scope); |
| if (providesRef.resolvedType != null && providesRef.resolvedType.isValidBinding()) { |
| // Verify duplicated tags |
| boolean found = false; |
| for (int j = 0; j < maxBindings && !found; j++) { |
| if (bindings[j].equals(providesRef.resolvedType)) { |
| scope.problemReporter().javadocDuplicatedProvidesTag(providesRef.sourceStart, providesRef.sourceEnd); |
| found = true; |
| } |
| } |
| if (!found) { |
| bindings[maxBindings++] = providesRef.resolvedType; |
| } |
| } |
| } catch (Exception e) { |
| scope.problemReporter().javadocInvalidProvidesClass(providesRef.sourceStart, providesRef.sourceEnd); |
| } |
| } |
| |
| // Look for undocumented uses |
| if (reportMissing) { |
| for (int i = 0; i < providesSize; i++) { |
| ProvidesStatement provides = moduleDecl.services[i]; |
| boolean found = false; |
| for (int j = 0; j < maxBindings && !found; j++) { |
| TypeBinding binding = bindings[j]; |
| if (provides.serviceInterface.getTypeBinding(scope).equals(binding)) { |
| found = true; |
| } |
| } |
| if (!found) { |
| scope.problemReporter().javadocMissingProvidesTag(provides.serviceInterface, provides.sourceStart, provides.sourceEnd, moduleDecl.binding.modifiers); |
| } |
| } |
| } |
| } |
| } |
| |
| /* |
| * Resolve @param tags for type parameters |
| */ |
| private void resolveTypeParameterTags(Scope scope, boolean reportMissing) { |
| int paramTypeParamLength = this.paramTypeParameters == null ? 0 : this.paramTypeParameters.length; |
| int paramReferencesLength = this.paramReferences == null ? 0 : this.paramReferences.length; |
| |
| // Get declaration infos |
| TypeParameter[] parameters = null; |
| TypeVariableBinding[] typeVariables = null; |
| Argument[] recordParameters = null; |
| int modifiers = -1; |
| switch (scope.kind) { |
| case Scope.METHOD_SCOPE: |
| AbstractMethodDeclaration 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++) { |
| JavadocSingleTypeReference param = this.paramTypeParameters[i]; |
| scope.problemReporter().javadocUnexpectedTag(param.tagSourceStart, param.tagSourceEnd); |
| } |
| return; |
| } |
| parameters = methodDeclaration.typeParameters(); |
| typeVariables = methodDeclaration.binding.typeVariables; |
| modifiers = methodDeclaration.binding.modifiers; |
| break; |
| case Scope.CLASS_SCOPE: |
| TypeDeclaration typeDeclaration = ((ClassScope) scope).referenceContext; |
| parameters = typeDeclaration.typeParameters; |
| typeVariables = typeDeclaration.binding.typeVariables; |
| modifiers = typeDeclaration.binding.modifiers; |
| if (typeDeclaration instanceof RecordDeclaration) { |
| RecordDeclaration recordDecl = (RecordDeclaration)typeDeclaration; |
| recordParameters = recordDecl.getArgs(); |
| } |
| break; |
| } |
| |
| // If no type variables then report a problem for each param type parameter tag |
| if ((recordParameters == null || recordParameters.length == 0) |
| && (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 record parameter |
| if (recordParameters != null) { |
| reportMissing = reportMissing && scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5; |
| int recordParametersLength = recordParameters.length; |
| String argNames[] = new String[paramReferencesLength]; |
| if (paramReferencesLength == 0) { |
| if (reportMissing) { |
| for (int i = 0, l=recordParametersLength; i<l; i++) { |
| scope.problemReporter().javadocMissingParamTag(recordParameters[i].name, recordParameters[i].sourceStart, recordParameters[i].sourceEnd, modifiers); |
| } |
| } |
| } else { |
| // Otherwise verify that all param tags match record args |
| // Scan all @param tags |
| for (int i = 0; i < paramReferencesLength; ++i) { |
| JavadocSingleNameReference param = this.paramReferences[i]; |
| String paramName = new String(param.getName()[0]); |
| // Verify duplicated tags |
| boolean duplicate = false; |
| for (int j = 0; j < i && !duplicate; j++) { |
| if (paramName.equals(argNames[j])) { |
| scope.problemReporter().javadocDuplicatedParamTag(param.token, param.sourceStart, param.sourceEnd, modifiers); |
| duplicate = true; |
| } |
| } |
| if (!duplicate) { |
| argNames[i] = paramName; |
| } |
| } |
| // Look for undocumented arguments |
| if (reportMissing) { |
| for (int i = 0; i < recordParameters.length; i++) { |
| Argument arg = recordParameters[i]; |
| boolean found = false; |
| for (int j = 0; j < paramReferencesLength && !found; j++) { |
| JavadocSingleNameReference param = this.paramReferences[j]; |
| String paramName = new String(param.getName()[0]); |
| if (paramName.equals(new String(arg.name))) { |
| found = true; |
| } |
| } |
| if (!found) { |
| scope.problemReporter().javadocMissingParamTag(arg.name, arg.sourceStart, arg.sourceEnd, modifiers); |
| } |
| } |
| } |
| // Look for param tags that specify non-existent arguments |
| for (int i = 0; i < paramReferencesLength; i++) { |
| JavadocSingleNameReference param = this.paramReferences[i]; |
| String paramName = new String(param.getName()[0]); |
| boolean found = false; |
| for (int j = 0; j < recordParameters.length; j++) { |
| Argument arg = recordParameters[j]; |
| if (paramName.equals(new String(arg.name))) { |
| found = true; |
| } |
| } |
| if (!found) { |
| scope.problemReporter().javadocInvalidParamTagName(param.sourceStart, param.sourceEnd); |
| } |
| } |
| } |
| } |
| |
| // If no param tags then report a problem for each declaration type parameter |
| if (parameters != null) { |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=324850, avoid secondary errors when <= 1.4 |
| reportMissing = reportMissing && scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5; |
| int typeParametersLength = parameters.length; |
| if (paramTypeParamLength == 0) { |
| if (reportMissing) { |
| for (int i = 0, l=typeParametersLength; 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]; |
| |
| // Scan all @param tags |
| for (int i = 0; i < paramTypeParamLength; i++) { |
| JavadocSingleTypeReference param = this.paramTypeParameters[i]; |
| TypeBinding paramBindind = param.internalResolveType(scope, 0); |
| if (paramBindind != null && paramBindind.isValidBinding()) { |
| if (paramBindind.isTypeVariable()) { |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=397888 |
| if (scope.compilerOptions().reportUnusedParameterIncludeDocCommentReference) { |
| TypeVariableBinding typeVariableBinding = (TypeVariableBinding) paramBindind; |
| typeVariableBinding.modifiers |= ExtraCompilerModifiers.AccLocallyUsed; |
| } |
| // Verify duplicated tags |
| boolean duplicate = false; |
| for (int j = 0; j < i && !duplicate; j++) { |
| if (TypeBinding.equalsEquals(bindings[j], param.resolvedType)) { |
| scope.problemReporter().javadocDuplicatedParamTag(param.token, param.sourceStart, param.sourceEnd, modifiers); |
| duplicate = true; |
| } |
| } |
| if (!duplicate) { |
| bindings[i] = (TypeVariableBinding) param.resolvedType; |
| } |
| } else { |
| scope.problemReporter().javadocUndeclaredParamTagName(param.token, param.sourceStart, param.sourceEnd, modifiers); |
| } |
| } |
| } |
| |
| // Look for undocumented type parameters |
| for (int i = 0; i < typeParametersLength; i++) { |
| TypeParameter parameter = parameters[i]; |
| boolean found = false; |
| for (int j = 0; j < paramTypeParamLength && !found; j++) { |
| if (TypeBinding.equalsEquals(parameter.binding, bindings[j])) { |
| found = true; |
| bindings[j] = null; |
| } |
| } |
| if (!found && reportMissing) { |
| scope.problemReporter().javadocMissingParamTag(parameter.name, parameter.sourceStart, parameter.sourceEnd, modifiers); |
| } |
| } |
| |
| // Report invalid param |
| for (int i=0; i<paramTypeParamLength; i++) { |
| if (bindings[i] != null) { |
| JavadocSingleTypeReference param = this.paramTypeParameters[i]; |
| scope.problemReporter().javadocUndeclaredParamTagName(param.token, param.sourceStart, param.sourceEnd, modifiers); |
| } |
| } |
| } |
| } |
| } |
| |
| /* |
| * Resolve @throws/@exception tags while method scope |
| */ |
| private void resolveThrowsTags(MethodScope methScope, boolean reportMissing) { |
| AbstractMethodDeclaration md = methScope.referenceMethod(); |
| 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.exceptionReferences[i]; |
| int start = typeRef.sourceStart; |
| int end = typeRef.sourceEnd; |
| if (typeRef instanceof JavadocQualifiedTypeReference) { |
| start = ((JavadocQualifiedTypeReference) typeRef).tagSourceStart; |
| end = ((JavadocQualifiedTypeReference) typeRef).tagSourceEnd; |
| } else if (typeRef instanceof JavadocSingleTypeReference) { |
| start = ((JavadocSingleTypeReference) typeRef).tagSourceStart; |
| end = ((JavadocSingleTypeReference) typeRef).tagSourceEnd; |
| } |
| methScope.problemReporter().javadocUnexpectedTag(start, end); |
| } |
| return; |
| } |
| |
| // If no throws tags then report a problem for each method thrown exception |
| int boundExceptionLength = (md.binding == null) ? 0 : md.binding.thrownExceptions.length; |
| int thrownExceptionLength = md.thrownExceptions == null ? 0 : md.thrownExceptions.length; |
| if (throwsTagsLength == 0) { |
| if (reportMissing) { |
| for (int i = 0; i < boundExceptionLength; i++) { |
| ReferenceBinding exceptionBinding = md.binding.thrownExceptions[i]; |
| if (exceptionBinding != null && exceptionBinding.isValidBinding()) { // flag only valid class name |
| int j=i; |
| while (j<thrownExceptionLength && TypeBinding.notEquals(exceptionBinding, md.thrownExceptions[j].resolvedType)) j++; |
| if (j<thrownExceptionLength) { |
| methScope.problemReporter().javadocMissingThrowsTag(md.thrownExceptions[j], md.binding.modifiers); |
| } |
| } |
| } |
| } |
| } else { |
| int maxRef = 0; |
| TypeReference[] typeReferences = new TypeReference[throwsTagsLength]; |
| |
| // Scan all @throws tags |
| for (int i = 0; i < throwsTagsLength; i++) { |
| TypeReference typeRef = this.exceptionReferences[i]; |
| typeRef.resolve(methScope); |
| TypeBinding typeBinding = typeRef.resolvedType; |
| |
| if (typeBinding != null && typeBinding.isValidBinding() && typeBinding.isClass()) { |
| // accept only valid class binding |
| typeReferences[maxRef++] = typeRef; |
| } |
| } |
| |
| // Look for undocumented thrown exception |
| for (int i = 0; i < boundExceptionLength; i++) { |
| ReferenceBinding exceptionBinding = md.binding.thrownExceptions[i]; |
| if (exceptionBinding != null) exceptionBinding = (ReferenceBinding) exceptionBinding.erasure(); |
| boolean found = false; |
| for (int j = 0; j < maxRef && !found; j++) { |
| if (typeReferences[j] != null) { |
| TypeBinding typeBinding = typeReferences[j].resolvedType; |
| if (TypeBinding.equalsEquals(exceptionBinding, typeBinding)) { |
| found = true; |
| typeReferences[j] = null; |
| } |
| } |
| } |
| if (!found && reportMissing) { |
| if (exceptionBinding != null && exceptionBinding.isValidBinding()) { // flag only valid class name |
| int k=i; |
| while (k<thrownExceptionLength && TypeBinding.notEquals(exceptionBinding, md.thrownExceptions[k].resolvedType)) k++; |
| if (k<thrownExceptionLength) { |
| methScope.problemReporter().javadocMissingThrowsTag(md.thrownExceptions[k], md.binding.modifiers); |
| } |
| } |
| } |
| } |
| |
| // Verify additional @throws tags |
| for (int i = 0; i < maxRef; i++) { |
| TypeReference typeRef = typeReferences[i]; |
| if (typeRef != null) { |
| boolean compatible = false; |
| // thrown exceptions subclasses are accepted |
| for (int j = 0; j<thrownExceptionLength && !compatible; j++) { |
| TypeBinding exceptionBinding = md.thrownExceptions[j].resolvedType; |
| if (exceptionBinding != null) { |
| compatible = typeRef.resolvedType.isCompatibleWith(exceptionBinding); |
| } |
| } |
| |
| // If not compatible only complain on unchecked exception |
| if (!compatible && !typeRef.resolvedType.isUncheckedException(false)) { |
| methScope.problemReporter().javadocInvalidThrowsClassName(typeRef, md.binding.modifiers); |
| } |
| } |
| } |
| } |
| } |
| |
| private void verifyTypeReference(Expression reference, Expression typeReference, Scope scope, boolean source15, ReferenceBinding resolvedType, int modifiers) { |
| if (resolvedType.isValidBinding()) { |
| int scopeModifiers = -1; |
| |
| // reference must have enough visibility to be used |
| if (!canBeSeen(scope.problemReporter().options.reportInvalidJavadocTagsVisibility, modifiers)) { |
| scope.problemReporter().javadocHiddenReference(typeReference.sourceStart, reference.sourceEnd, scope, modifiers); |
| return; |
| } |
| |
| // type reference must have enough visibility to be used |
| if (reference != typeReference) { |
| if (!canBeSeen(scope.problemReporter().options.reportInvalidJavadocTagsVisibility, resolvedType.modifiers)) { |
| scope.problemReporter().javadocHiddenReference(typeReference.sourceStart, typeReference.sourceEnd, scope, resolvedType.modifiers); |
| return; |
| } |
| } |
| |
| // member types |
| if (resolvedType.isMemberType()) { |
| ReferenceBinding topLevelType = resolvedType; |
| // rebuild and store (in reverse order) compound name to handle embedded inner class |
| int packageLength = topLevelType.fPackage.compoundName.length; |
| int depth = resolvedType.depth(); |
| int idx = depth + packageLength; |
| char[][] computedCompoundName = new char[idx+1][]; |
| computedCompoundName[idx] = topLevelType.sourceName; |
| while (topLevelType.enclosingType() != null) { |
| topLevelType = topLevelType.enclosingType(); |
| computedCompoundName[--idx] = topLevelType.sourceName; |
| } |
| |
| // add package information |
| for (int i = packageLength; --i >= 0;) { |
| computedCompoundName[--idx] = topLevelType.fPackage.compoundName[i]; |
| } |
| |
| if (scope.kind != Scope.MODULE_SCOPE) { |
| ClassScope topLevelScope = scope.classScope(); |
| // when scope is not on compilation unit type, then inner class may not be visible... |
| if (topLevelScope.parent.kind != Scope.COMPILATION_UNIT_SCOPE || |
| !CharOperation.equals(topLevelType.sourceName, topLevelScope.referenceContext.name)) { |
| topLevelScope = topLevelScope.outerMostClassScope(); |
| if (typeReference instanceof JavadocSingleTypeReference) { |
| // inner class single reference can only be done in same unit |
| if ((!source15 && depth == 1) || TypeBinding.notEquals(topLevelType, topLevelScope.referenceContext.binding)) { |
| // search for corresponding import |
| boolean hasValidImport = false; |
| if (source15) { |
| CompilationUnitScope unitScope = topLevelScope.compilationUnitScope(); |
| ImportBinding[] imports = unitScope.imports; |
| int length = imports == null ? 0 : imports.length; |
| mainLoop: for (int i=0; i<length; i++) { |
| char[][] compoundName = imports[i].compoundName; |
| int compoundNameLength = compoundName.length; |
| if ((imports[i].onDemand && compoundNameLength == computedCompoundName.length-1) |
| || (compoundNameLength == computedCompoundName.length)) { |
| for (int j = compoundNameLength; --j >= 0;) { |
| if (CharOperation.equals(imports[i].compoundName[j], computedCompoundName[j])) { |
| if (j == 0) { |
| hasValidImport = true; |
| ImportReference importReference = imports[i].reference; |
| if (importReference != null) { |
| importReference.bits |= ASTNode.Used; |
| } |
| break mainLoop; |
| } |
| } else { |
| break; |
| } |
| } |
| } |
| } |
| if (!hasValidImport) { |
| if (scopeModifiers == -1) scopeModifiers = scope.getDeclarationModifiers(); |
| scope.problemReporter().javadocInvalidMemberTypeQualification(typeReference.sourceStart, typeReference.sourceEnd, scopeModifiers); |
| } |
| } else { |
| if (scopeModifiers == -1) scopeModifiers = scope.getDeclarationModifiers(); |
| scope.problemReporter().javadocInvalidMemberTypeQualification(typeReference.sourceStart, typeReference.sourceEnd, scopeModifiers); |
| return; |
| } |
| } |
| } |
| } |
| if (typeReference instanceof JavadocQualifiedTypeReference && !scope.isDefinedInSameUnit(resolvedType)) { |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=222188 |
| // partially qualified references from a different CU should be warned |
| char[][] typeRefName = ((JavadocQualifiedTypeReference) typeReference).getTypeName(); |
| int skipLength = 0; |
| if (topLevelScope.getCurrentPackage() == resolvedType.getPackage() |
| && typeRefName.length < computedCompoundName.length) { |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=221539: references can be partially qualified |
| // in same package and hence if the package name is not given, ignore package name check |
| skipLength = resolvedType.fPackage.compoundName.length; |
| } |
| boolean valid = true; |
| if (typeRefName.length == computedCompoundName.length - skipLength) { |
| checkQualification: for (int i = 0; i < typeRefName.length; i++) { |
| if (!CharOperation.equals(typeRefName[i], computedCompoundName[i + skipLength])) { |
| valid = false; |
| break checkQualification; |
| } |
| } |
| } else { |
| valid = false; |
| } |
| // report invalid reference |
| if (!valid) { |
| if (scopeModifiers == -1) scopeModifiers = scope.getDeclarationModifiers(); |
| scope.problemReporter().javadocInvalidMemberTypeQualification(typeReference.sourceStart, typeReference.sourceEnd, scopeModifiers); |
| return; |
| } |
| } |
| } |
| } |
| /* |
| * https://bugs.eclipse.org/bugs/show_bug.cgi?id=286918 |
| * |
| * We are concerned only about the Single type references (i.e. without any package) If they are not in default package, |
| * then report an error. References with qualified yet incorrect names would have already been taken care of. |
| */ |
| if (scope.referenceCompilationUnit().isPackageInfo() && typeReference instanceof JavadocSingleTypeReference) { |
| if (resolvedType.fPackage.compoundName.length > 0) { |
| scope.problemReporter().javadocInvalidReference(typeReference.sourceStart, typeReference.sourceEnd); |
| return; // Not really needed - just in case more code added in future |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void traverse(ASTVisitor visitor, BlockScope scope) { |
| if (visitor.visit(this, scope)) { |
| if (this.paramReferences != null) { |
| for (int i = 0, length = this.paramReferences.length; i < length; i++) { |
| this.paramReferences[i].traverse(visitor, scope); |
| } |
| } |
| if (this.paramTypeParameters != null) { |
| for (int i = 0, length = this.paramTypeParameters.length; i < length; i++) { |
| this.paramTypeParameters[i].traverse(visitor, scope); |
| } |
| } |
| if (this.returnStatement != null) { |
| this.returnStatement.traverse(visitor, scope); |
| } |
| if (this.exceptionReferences != null) { |
| for (int i = 0, length = this.exceptionReferences.length; i < length; i++) { |
| this.exceptionReferences[i].traverse(visitor, scope); |
| } |
| } |
| if (this.seeReferences != null) { |
| for (int i = 0, length = this.seeReferences.length; i < length; i++) { |
| this.seeReferences[i].traverse(visitor, scope); |
| } |
| } |
| } |
| visitor.endVisit(this, scope); |
| } |
| public void traverse(ASTVisitor visitor, ClassScope scope) { |
| if (visitor.visit(this, scope)) { |
| if (this.paramReferences != null) { |
| for (int i = 0, length = this.paramReferences.length; i < length; i++) { |
| this.paramReferences[i].traverse(visitor, scope); |
| } |
| } |
| if (this.paramTypeParameters != null) { |
| for (int i = 0, length = this.paramTypeParameters.length; i < length; i++) { |
| this.paramTypeParameters[i].traverse(visitor, scope); |
| } |
| } |
| if (this.returnStatement != null) { |
| this.returnStatement.traverse(visitor, scope); |
| } |
| if (this.exceptionReferences != null) { |
| for (int i = 0, length = this.exceptionReferences.length; i < length; i++) { |
| this.exceptionReferences[i].traverse(visitor, scope); |
| } |
| } |
| if (this.seeReferences != null) { |
| for (int i = 0, length = this.seeReferences.length; i < length; i++) { |
| this.seeReferences[i].traverse(visitor, scope); |
| } |
| } |
| } |
| visitor.endVisit(this, scope); |
| } |
| } |