/* * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.google.gwt.dev.javac; import com.google.gwt.dev.jdt.TypeRefVisitor; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation; import org.eclipse.jdt.internal.compiler.ast.NormalAnnotation; import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Check a {@link CompilationUnitDeclaration} for references to binary types * outside the context of an annotation. */ public class BinaryTypeReferenceRestrictionsChecker { /** * Records the location from which a {@link BinaryTypeBinding} is referenced. */ static class BinaryTypeReferenceSite { private final BinaryTypeBinding binaryTypeBinding; private final Expression expression; BinaryTypeReferenceSite(Expression expression, BinaryTypeBinding binaryTypeBinding) { this.expression = expression; this.binaryTypeBinding = binaryTypeBinding; } public BinaryTypeBinding getBinaryTypeBinding() { return binaryTypeBinding; } public Expression getExpression() { return expression; } } /** * Visits a {@link CompilationUnitDeclaration} and records all expressions * which use a {@link BinaryTypeBinding} that are not part of an annotation * context. */ static class BinaryTypeReferenceVisitor extends TypeRefVisitor { private final List<BinaryTypeReferenceSite> binaryTypeReferenceSites; public BinaryTypeReferenceVisitor(CompilationUnitDeclaration cud, List<BinaryTypeReferenceSite> binaryTypeReferenceSites) { super(cud); this.binaryTypeReferenceSites = binaryTypeReferenceSites; } @Override public boolean visit(MarkerAnnotation annotation, BlockScope scope) { // Ignore annotations return false; } @Override public boolean visit(NormalAnnotation annotation, BlockScope scope) { // Ignore annotations return false; } @Override public boolean visit(SingleMemberAnnotation annotation, BlockScope scope) { // Ignore annotations return false; } @Override protected void onBinaryTypeRef(BinaryTypeBinding binding, CompilationUnitDeclaration unitOfReferrer, Expression expression) { if (expression.constant != null && expression.constant != Constant.NotAConstant) { // Const expressions are ok, no source needed. return; } binaryTypeReferenceSites.add(new BinaryTypeReferenceSite(expression, binding)); } @Override protected void onTypeRef(SourceTypeBinding referencedType, CompilationUnitDeclaration unitOfReferrer) { // do nothing } } /** * Scans a {@link CompilationUnitDeclaration} for expressions that use * {@link BinaryTypeBinding}s outside the context of an annotation. An error * is reported against the {@link CompilationUnitDeclaration} for the first * instance of each unique {@link BinaryTypeBinding}. */ public static void check(CompilationUnitDeclaration cud) { List<BinaryTypeReferenceSite> binaryTypeReferenceSites = findAllBinaryTypeReferenceSites(cud); Set<BinaryTypeBinding> alreadySeenTypeBindings = new HashSet<BinaryTypeBinding>(); for (BinaryTypeReferenceSite binaryTypeReferenceSite : binaryTypeReferenceSites) { BinaryTypeBinding binaryTypeBinding = binaryTypeReferenceSite.getBinaryTypeBinding(); if (alreadySeenTypeBindings.contains(binaryTypeBinding)) { continue; } alreadySeenTypeBindings.add(binaryTypeBinding); String fileName = String.valueOf(binaryTypeBinding.getFileName()); if (fileName.endsWith(".java")) { // This binary name is valid; it is a reference to a unit that was // compiled in a previous JDT run. continue; } String qualifiedTypeName = binaryTypeBinding.debugName(); String error = formatBinaryTypeRefErrorMessage(qualifiedTypeName); // TODO(mmendez): provide extra help info? GWTProblem.recordError(binaryTypeReferenceSite.getExpression(), cud, error, null); } } static List<BinaryTypeReferenceSite> findAllBinaryTypeReferenceSites( CompilationUnitDeclaration cud) { List<BinaryTypeReferenceSite> binaryTypeReferenceSites = new ArrayList<BinaryTypeReferenceSite>(); BinaryTypeReferenceVisitor binaryTypeReferenceVisitor = new BinaryTypeReferenceVisitor( cud, binaryTypeReferenceSites); cud.traverse(binaryTypeReferenceVisitor, cud.scope); return binaryTypeReferenceSites; } static String formatBinaryTypeRefErrorMessage(String qualifiedTypeName) { return "No source code is available for type " + qualifiedTypeName + "; did you forget to inherit a required module?"; } private BinaryTypeReferenceRestrictionsChecker() { // Not instantiable } }