/* * The JastAdd Extensible Java Compiler (http://jastadd.org) is covered * by the modified BSD License. You should have received a copy of the * modified BSD license with this compiler. * * Copyright (c) 2005-2008, Torbjorn Ekman * All rights reserved. */ aspect TypeHierarchyCheck { inh String Expr.methodHost(); eq TypeDecl.getBodyDecl().methodHost() = typeName(); eq AbstractDot.getRight().methodHost() = getLeft().type().typeName(); eq Program.getChild().methodHost() { throw new Error("Needs extra equation for methodHost()"); } eq MethodAccess.getChild().methodHost() = unqualifiedScope().methodHost(); eq ConstructorAccess.getChild().methodHost() = unqualifiedScope().methodHost(); syn boolean Expr.isUnknown() = type().isUnknown(); eq PackageAccess.isUnknown() = !hasPackage(packageName()); public void MethodAccess.nameCheck() { if(isQualified() && qualifier().isPackageAccess() && !qualifier().isUnknown()) error("The method " + decl().signature() + " can not be qualified by a package name."); if(isQualified() && decl().isAbstract() && qualifier().isSuperAccess()) error("may not access abstract methods in superclass"); if(decls().isEmpty() && (!isQualified() || !qualifier().isUnknown())) { StringBuffer s = new StringBuffer(); s.append("no method named " + name()); s.append("("); for(int i = 0; i < getNumArg(); i++) { if(i != 0) s.append(", "); s.append(getArg(i).type().typeName()); } s.append(")" + " in " + methodHost() + " matches."); if(singleCandidateDecl() != null) s.append(" However, there is a method " + singleCandidateDecl().signature()); error(s.toString()); } if(decls().size() > 1) { boolean allAbstract = true; for(Iterator iter = decls().iterator(); iter.hasNext() && allAbstract; ) { MethodDecl m = (MethodDecl)iter.next(); if(!m.isAbstract() && !m.hostType().isObject()) allAbstract = false; } if(!allAbstract && validArgs()) { StringBuffer s = new StringBuffer(); s.append("several most specific methods for " + this + "\n"); for(Iterator iter = decls().iterator(); iter.hasNext(); ) { MethodDecl m = (MethodDecl)iter.next(); s.append(" " + m.signature() + " in " + m.hostType().typeName() + "\n"); } error(s.toString()); } } } public void SuperConstructorAccess.nameCheck() { super.nameCheck(); // 8.8.5.1 TypeDecl c = hostType(); TypeDecl s = c.isClassDecl() && ((ClassDecl)c).hasSuperclass() ? ((ClassDecl)c).superclass() : unknownType(); if(isQualified()) { if(!s.isInnerType() || s.inStaticContext()) error("the super type " + s.typeName() + " of " + c.typeName() + " is not an inner class"); else if(!qualifier().type().instanceOf(s.enclosingType())) error("The type of this primary expression, " + qualifier().type().typeName() + " is not enclosing the super type, " + s.typeName() + ", of " + c.typeName()); } if(!isQualified() && s.isInnerType()) { if(!c.isInnerType()) { error("no enclosing instance for " + s.typeName() + " when accessed in " + this); } } if(s.isInnerType() && hostType().instanceOf(s.enclosingType())) error("cannot reference this before supertype constructor has been called"); } public void SuperAccess.nameCheck() { if(isQualified()) { if(!hostType().isInnerTypeOf(decl()) && hostType() != decl()) error("qualified super must name an enclosing type"); if(inStaticContext()) { error("*** Qualified super may not occur in static context"); } } // 8.8.5.1 if(inExplicitConstructorInvocation() && hostType().instanceOf(decl().hostType()) ) error("super may not be accessed in an explicit constructor invocation"); // 8.4.3.2 if(inStaticContext()) error("super may not be accessed in a static context"); } public void ThisAccess.nameCheck() { // 8.8.5.1 if(inExplicitConstructorInvocation() && hostType() == type()) error("this may not be accessed in an explicit constructor invocation"); else if(isQualified()) { // 15.8.4 if(inStaticContext()) error("qualified this may not occur in static context"); else if(!hostType().isInnerTypeOf(decl()) && hostType() != decl()) error("qualified this must name an enclosing type: " + getParent()); } // 8.4.3.2 else if(!isQualified() && inStaticContext()) error("this may not be accessed in static context: " + enclosingStmt()); } // 8.8.5.1 inh boolean VarAccess.inExplicitConstructorInvocation(); inh boolean MethodAccess.inExplicitConstructorInvocation(); inh boolean SuperAccess.inExplicitConstructorInvocation(); inh boolean ThisAccess.inExplicitConstructorInvocation(); inh boolean ClassInstanceExpr.inExplicitConstructorInvocation(); inh lazy boolean TypeDecl.inExplicitConstructorInvocation(); eq Program.getChild().inExplicitConstructorInvocation() = false; eq ConstructorAccess.getArg().inExplicitConstructorInvocation() = true; eq SuperConstructorAccess.getArg().inExplicitConstructorInvocation() = true; eq ConstructorDecl.getConstructorInvocation().inExplicitConstructorInvocation() = true; inh boolean Expr.inStaticContext(); // SuperAccess, ThisAccess, ClassInstanceExpr, MethodAccess inh lazy boolean TypeDecl.inStaticContext(); eq Program.getChild().inStaticContext() = false; eq TypeDecl.getBodyDecl().inStaticContext() = isStatic() || inStaticContext(); eq StaticInitializer.getBlock().inStaticContext() = true; eq InstanceInitializer.getBlock().inStaticContext() = false; eq FieldDeclaration.getInit().inStaticContext() = isStatic() || hostType().isInterfaceDecl(); eq MethodDecl.getBlock().inStaticContext() = isStatic(); eq ConstructorDecl.getBlock().inStaticContext() = false; eq ConstructorDecl.getConstructorInvocation().inStaticContext() = false; eq MemberClassDecl.getClassDecl().inStaticContext() = false; eq ClassInstanceExpr.getTypeDecl().inStaticContext() = isQualified() ? qualifier().staticContextQualifier() : inStaticContext(); syn boolean Expr.staticContextQualifier() = false; eq ParExpr.staticContextQualifier() = getExpr().staticContextQualifier(); eq CastExpr.staticContextQualifier() = getExpr().staticContextQualifier(); eq AbstractDot.staticContextQualifier() = lastAccess().staticContextQualifier(); eq TypeAccess.staticContextQualifier() = true; eq ArrayTypeAccess.staticContextQualifier() = true; public void TypeDecl.typeCheck() { // 8.4.6.4 & 9.4.1 for(Iterator iter1 = localMethodsIterator(); iter1.hasNext(); ) { MethodDecl m = (MethodDecl)iter1.next(); ASTNode target = m.hostType() == this ? (ASTNode)m : (ASTNode)this; //for(Iterator i2 = overrides(m).iterator(); i2.hasNext(); ) { for(Iterator i2 = ancestorMethods(m.signature()).iterator(); i2.hasNext(); ) { MethodDecl decl = (MethodDecl)i2.next(); if(m.overrides(decl)) { // 8.4.6.1 if(!m.isStatic() && decl.isStatic()) target.error("an instance method may not override a static method"); // regardless of overriding // 8.4.6.3 if(!m.mayOverrideReturn(decl)) target.error("the return type of method " + m.signature() + " in " + m.hostType().typeName() + " does not match the return type of method " + decl.signature() + " in " + decl.hostType().typeName() + " and may thus not be overriden"); // regardless of overriding // 8.4.4 for(int i = 0; i < m.getNumException(); i++) { Access e = m.getException(i); boolean found = false; for(int j = 0; !found && j < decl.getNumException(); j++) { if(e.type().instanceOf(decl.getException(j).type())) found = true; } if(!found && e.type().isUncheckedException()) target.error(m.signature() + " in " + m.hostType().typeName() + " may not throw more checked exceptions than overridden method " + decl.signature() + " in " + decl.hostType().typeName()); } // 8.4.6.3 if(decl.isPublic() && !m.isPublic()) target.error("overriding access modifier error"); // 8.4.6.3 if(decl.isProtected() && !(m.isPublic() || m.isProtected())) target.error("overriding access modifier error"); // 8.4.6.3 if((!decl.isPrivate() && !decl.isProtected() && !decl.isPublic()) && m.isPrivate()) target.error("overriding access modifier error"); // regardless of overriding if(decl.isFinal()) target.error("method " + m.signature() + " in " + hostType().typeName() + " can not override final method " + decl.signature() + " in " + decl.hostType().typeName()); } if(m.hides(decl)) { // 8.4.6.2 if(m.isStatic() && !decl.isStatic()) target.error("a static method may not hide an instance method"); // 8.4.6.3 if(!m.mayOverrideReturn(decl)) target.error("can not hide a method with a different return type"); // 8.4.4 for(int i = 0; i < m.getNumException(); i++) { Access e = m.getException(i); boolean found = false; for(int j = 0; !found && j < decl.getNumException(); j++) { if(e.type().instanceOf(decl.getException(j).type())) found = true; } if(!found) target.error("may not throw more checked exceptions than hidden method"); } // 8.4.6.3 if(decl.isPublic() && !m.isPublic()) target.error("hiding access modifier error: public method " + decl.signature() + " in " + decl.hostType().typeName() + " is hidden by non public method " + m.signature() + " in " + m.hostType().typeName()); // 8.4.6.3 if(decl.isProtected() && !(m.isPublic() || m.isProtected())) target.error("hiding access modifier error: protected method " + decl.signature() + " in " + decl.hostType().typeName() + " is hidden by non (public|protected) method " + m.signature() + " in " + m.hostType().typeName()); // 8.4.6.3 if((!decl.isPrivate() && !decl.isProtected() && !decl.isPublic()) && m.isPrivate()) target.error("hiding access modifier error: default method " + decl.signature() + " in " + decl.hostType().typeName() + " is hidden by private method " + m.signature() + " in " + m.hostType().typeName()); if(decl.isFinal()) target.error("method " + m.signature() + " in " + hostType().typeName() + " can not hide final method " + decl.signature() + " in " + decl.hostType().typeName()); } } } } syn boolean MethodDecl.mayOverrideReturn(MethodDecl m) = type() == m.type(); public void ClassDecl.nameCheck() { super.nameCheck(); if(hasSuperClassAccess() && !getSuperClassAccess().type().isClassDecl()) error("class may only inherit a class and not " + getSuperClassAccess().type().typeName()); if(isObject() && hasSuperClassAccess()) error("class Object may not have superclass"); if(isObject() && getNumImplements() != 0) error("class Object may not implement interfaces"); // 8.1.3 if(isCircular()) error("circular inheritance dependency in " + typeName()); // 8.1.4 HashSet set = new HashSet(); for(int i = 0; i < getNumImplements(); i++) { TypeDecl decl = getImplements(i).type(); if(!decl.isInterfaceDecl() && !decl.isUnknown()) error("type " + fullName() + " tries to implement non interface type " + decl.fullName()); if(set.contains(decl)) error("type " + decl.fullName() + " mentionened multiple times in implements clause"); set.add(decl); } for(Iterator iter = interfacesMethodsIterator(); iter.hasNext(); ) { MethodDecl m = (MethodDecl)iter.next(); if(localMethodsSignature(m.signature()).isEmpty()) { SimpleSet s = superclass().methodsSignature(m.signature()); for(Iterator i2 = s.iterator(); i2.hasNext(); ) { MethodDecl n = (MethodDecl)i2.next(); if(n.accessibleFrom(this)) { interfaceMethodCompatibleWithInherited(m, n); } } if(s.isEmpty()) { for(Iterator i2 = interfacesMethodsSignature(m.signature()).iterator(); i2.hasNext(); ) { MethodDecl n = (MethodDecl)i2.next(); if(!n.mayOverrideReturn(m) && !m.mayOverrideReturn(n)) error("Xthe return type of method " + m.signature() + " in " + m.hostType().typeName() + " does not match the return type of method " + n.signature() + " in " + n.hostType().typeName() + " and may thus not be overriden"); } } } } } private void ClassDecl.interfaceMethodCompatibleWithInherited(MethodDecl m, MethodDecl n) { if(n.isStatic()) error("Xa static method may not hide an instance method"); if(!n.isAbstract() && !n.isPublic()) error("Xoverriding access modifier error for " + m.signature() + " in " + m.hostType().typeName() + " and " + n.hostType().typeName()); if(!n.mayOverrideReturn(m) && !m.mayOverrideReturn(m)) error("Xthe return type of method " + m.signature() + " in " + m.hostType().typeName() + " does not match the return type of method " + n.signature() + " in " + n.hostType().typeName() + " and may thus not be overriden"); if(!n.isAbstract()) { // n implements and overrides method m in the interface // may not throw more checked exceptions for(int i = 0; i < n.getNumException(); i++) { Access e = n.getException(i); boolean found = false; for(int j = 0; !found && j < m.getNumException(); j++) { if(e.type().instanceOf(m.getException(j).type())) found = true; } if(!found && e.type().isUncheckedException()) error("X" + n.signature() + " in " + n.hostType().typeName() + " may not throw more checked exceptions than overridden method " + m.signature() + " in " + m.hostType().typeName()); } } } public void InterfaceDecl.nameCheck() { super.nameCheck(); if(isCircular()) error("circular inheritance dependency in " + typeName()); else { for(int i = 0; i < getNumSuperInterfaceId(); i++) { TypeDecl typeDecl = getSuperInterfaceId(i).type(); if(typeDecl.isCircular()) error("circular inheritance dependency in " + typeName()); } } for(Iterator iter = methodsSignatureMap().values().iterator(); iter.hasNext(); ) { SimpleSet set = (SimpleSet)iter.next(); if(set.size() > 1) { Iterator i2 = set.iterator(); MethodDecl m = (MethodDecl)i2.next(); while(i2.hasNext()) { MethodDecl n = (MethodDecl)i2.next(); if(!n.mayOverrideReturn(m) && !m.mayOverrideReturn(n)) error("multiply inherited methods with the same signature must have the same return type"); } } } } }