001 /* Copyright (c) 2005-2008, Torbjorn Ekman 002 * All rights reserved. 003 * 004 * Redistribution and use in source and binary forms, with or without 005 * modification, are permitted provided that the following conditions are met: 006 * 007 * 1. Redistributions of source code must retain the above copyright notice, 008 * this list of conditions and the following disclaimer. 009 * 010 * 2. Redistributions in binary form must reproduce the above copyright notice, 011 * this list of conditions and the following disclaimer in the documentation 012 * and/or other materials provided with the distribution. 013 * 014 * 3. Neither the name of the copyright holder nor the names of its 015 * contributors may be used to endorse or promote products derived from this 016 * software without specific prior written permission. 017 * 018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 020 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 021 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 022 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 023 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 024 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 025 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 026 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 028 * POSSIBILITY OF SUCH DAMAGE. 029 */ 030 031 aspect TypeHierarchyCheck { 032 inh String Expr.methodHost(); 033 eq TypeDecl.getChild().methodHost() = typeName(); 034 eq AbstractDot.getRight().methodHost() = getLeft().type().typeName(); 035 eq Program.getChild().methodHost() { 036 throw new Error("Needs extra equation for methodHost()"); 037 } 038 eq MethodAccess.getChild().methodHost() = unqualifiedScope().methodHost(); 039 eq ConstructorAccess.getChild().methodHost() = unqualifiedScope().methodHost(); 040 041 syn boolean Expr.isUnknown() = type().isUnknown(); 042 eq PackageAccess.isUnknown() = !hasPackage(packageName()); 043 044 public void MethodAccess.nameCheck() { 045 if (isQualified() && qualifier().isPackageAccess() && !qualifier().isUnknown()) { 046 errorf("The method %s can not be qualified by a package name.", decl().fullSignature()); 047 } 048 if (isQualified() && decl().isAbstract() && qualifier().isSuperAccess()) { 049 error("may not access abstract methods in superclass"); 050 } 051 if (decls().isEmpty() && (!isQualified() || !qualifier().isUnknown())) { 052 StringBuilder sb = new StringBuilder(); 053 sb.append("no method named " + name()); 054 sb.append("("); 055 for (int i = 0; i < getNumArg(); i++) { 056 TypeDecl argType = getArg(i).type(); 057 if (argType.isVoid()) { 058 // error will be reported for the void argument in typeCheck 059 // so we return now to avoid confusing double errors 060 return; 061 } 062 if (i != 0) { 063 sb.append(", "); 064 } 065 sb.append(argType.typeName()); 066 } 067 sb.append(")" + " in " + methodHost() + " matches."); 068 if (singleCandidateDecl() != null) { 069 sb.append(" However, there is a method " + singleCandidateDecl().fullSignature()); 070 } 071 error(sb.toString()); 072 } 073 if (decls().size() > 1) { 074 boolean allAbstract = true; 075 for (Iterator iter = decls().iterator(); iter.hasNext() && allAbstract; ) { 076 MethodDecl m = (MethodDecl) iter.next(); 077 if (!m.isAbstract() && !m.hostType().isObject()) { 078 allAbstract = false; 079 } 080 } 081 if (!allAbstract && validArgs()) { 082 StringBuilder sb = new StringBuilder(); 083 sb.append("several most specific methods for " + this.prettyPrint() + "\n"); 084 for (Iterator iter = decls().iterator(); iter.hasNext(); ) { 085 MethodDecl m = (MethodDecl) iter.next(); 086 sb.append(" " + m.fullSignature() + " in " + m.hostType().typeName() + "\n"); 087 } 088 error(sb.toString()); 089 } 090 091 } 092 } 093 094 public void SuperConstructorAccess.nameCheck() { 095 super.nameCheck(); 096 // JLS 5?: 8.8.5.1 097 // JLS 7: 8.8.7.1 098 TypeDecl c = hostType(); 099 TypeDecl s = c.isClassDecl() ? ((ClassDecl) c).superclass() : unknownType(); 100 if (isQualified()) { 101 if (!s.isInnerType() || s.inStaticContext()) { 102 errorf("the super type %s of %s is not an inner class", s.typeName(), c.typeName()); 103 } else if (!qualifier().type().instanceOf(s.enclosingType())) { 104 errorf("The type of this primary expression, %s is not enclosing the super type, %s, of %s", 105 qualifier().type().typeName(), s.typeName(), c.typeName()); 106 } 107 } 108 if (!isQualified() && s.isInnerType()) { 109 if (!c.isInnerType()) { 110 errorf("no enclosing instance for %s when accessed in %s", s.typeName(), this.prettyPrint()); 111 } 112 } 113 if (s.isInnerType() && hostType().instanceOf(s.enclosingType())) { 114 error("cannot reference 'this' before supertype constructor has been called"); 115 } 116 } 117 118 public void SuperAccess.nameCheck() { 119 if (isQualified()) { 120 if (!hostType().isInnerTypeOf(decl()) && hostType() != decl()) { 121 error("qualified super must name an enclosing type"); 122 } 123 if (inStaticContext()) { 124 error("*** Qualified super may not occur in static context"); 125 } 126 } 127 // 8.8.5.1 128 // JLSv7 8.8.7.1 129 TypeDecl constructorHostType = enclosingExplicitConstructorHostType(); 130 if (constructorHostType != null && (constructorHostType == decl())) { 131 error("super may not be accessed in an explicit constructor invocation"); 132 } 133 // 8.4.3.2 134 if (inStaticContext()) { 135 error("super may not be accessed in a static context"); 136 } 137 } 138 139 public void ThisAccess.nameCheck() { 140 // 8.8.5.1 141 // JLSv7 8.8.7.1 142 TypeDecl constructorHostType = enclosingExplicitConstructorHostType(); 143 if (constructorHostType != null && (constructorHostType == decl())) { 144 error("this may not be accessed in an explicit constructor invocation"); 145 } else if (isQualified()) { 146 // 15.8.4 147 if (inStaticContext()) { 148 error("qualified this may not occur in static context"); 149 } else if (!hostType().isInnerTypeOf(decl()) && hostType() != decl()) { 150 errorf("qualified this access must name an enclosing type which %s is not", 151 decl().typeName()); 152 } 153 } else if (!isQualified() && inStaticContext()) { 154 // 8.4.3.2 155 error("this access may not be used in a static context"); 156 } 157 } 158 159 // 8.8.5.1 160 inh boolean VarAccess.inExplicitConstructorInvocation(); 161 inh boolean MethodAccess.inExplicitConstructorInvocation(); 162 inh boolean SuperAccess.inExplicitConstructorInvocation(); 163 inh boolean ThisAccess.inExplicitConstructorInvocation(); 164 inh boolean ClassInstanceExpr.inExplicitConstructorInvocation(); 165 inh lazy boolean TypeDecl.inExplicitConstructorInvocation(); 166 eq Program.getChild().inExplicitConstructorInvocation() = false; 167 168 eq ConstructorAccess.getArg().inExplicitConstructorInvocation() = true; 169 eq SuperConstructorAccess.getArg().inExplicitConstructorInvocation() = true; 170 eq ConstructorDecl.getParsedConstructorInvocation().inExplicitConstructorInvocation() = true; 171 eq ConstructorDecl.getImplicitConstructorInvocation().inExplicitConstructorInvocation() = true; 172 173 inh TypeDecl SuperAccess.enclosingExplicitConstructorHostType(); 174 inh TypeDecl ThisAccess.enclosingExplicitConstructorHostType(); 175 176 eq Program.getChild().enclosingExplicitConstructorHostType() = null; 177 eq ConstructorAccess.getArg().enclosingExplicitConstructorHostType() = hostType(); 178 eq SuperConstructorAccess.getArg().enclosingExplicitConstructorHostType() = hostType(); 179 eq ConstructorDecl.getParsedConstructorInvocation().enclosingExplicitConstructorHostType() = hostType(); 180 eq ConstructorDecl.getImplicitConstructorInvocation().enclosingExplicitConstructorHostType() = hostType(); 181 182 inh boolean Expr.inStaticContext(); // SuperAccess, ThisAccess, ClassInstanceExpr, MethodAccess 183 inh lazy boolean TypeDecl.inStaticContext(); 184 185 eq Program.getChild().inStaticContext() = false; 186 eq TypeDecl.getChild().inStaticContext() = isStatic() || inStaticContext(); 187 eq StaticInitializer.getBlock().inStaticContext() = true; 188 eq InstanceInitializer.getBlock().inStaticContext() = false; 189 eq FieldDeclaration.getInit().inStaticContext() = isStatic(); 190 eq MethodDecl.getBlock().inStaticContext() = isStatic(); 191 eq ConstructorDecl.getBlock().inStaticContext() = false; 192 eq ConstructorDecl.getParsedConstructorInvocation().inStaticContext() = false; 193 eq ConstructorDecl.getImplicitConstructorInvocation().inStaticContext() = false; 194 eq MemberClassDecl.getClassDecl().inStaticContext() = false; 195 196 eq ClassInstanceExpr.getTypeDecl().inStaticContext() = isQualified() ? 197 qualifier().staticContextQualifier() : inStaticContext(); 198 199 syn boolean Expr.staticContextQualifier() = false; 200 eq ParExpr.staticContextQualifier() = getExpr().staticContextQualifier(); 201 eq CastExpr.staticContextQualifier() = getExpr().staticContextQualifier(); 202 eq AbstractDot.staticContextQualifier() = lastAccess().staticContextQualifier(); 203 eq TypeAccess.staticContextQualifier() = true; 204 eq ArrayTypeAccess.staticContextQualifier() = true; 205 206 public void TypeDecl.typeCheck() { 207 // 8.4.6.4 & 9.4.1 208 for (Iterator iter1 = localMethodsIterator(); iter1.hasNext(); ) { 209 MethodDecl m = (MethodDecl) iter1.next(); 210 ASTNode target = m.hostType() == this ? (ASTNode) m : (ASTNode) this; 211 212 for (Iterator i2 = ancestorMethods(m.signature()).iterator(); i2.hasNext(); ) { 213 MethodDecl decl = (MethodDecl) i2.next(); 214 if (m.overrides(decl)) { 215 // 8.4.6.1 216 if (!m.isStatic() && decl.isStatic()) { 217 target.error("an instance method may not override a static method"); 218 } 219 220 // regardless of overriding 221 // 8.4.6.3 222 if (!m.mayOverride(decl)) { 223 target.errorf("the return type of method %s in %s does not match the return type of" 224 + " method %s in %s and may thus not be overridden", 225 m.fullSignature(), m.hostType().typeName(), decl.fullSignature(), 226 decl.hostType().typeName()); 227 } 228 229 // regardless of overriding 230 // 8.4.4 231 for (Access e: m.getExceptionList()) { 232 if (e.type().isCheckedException()) { 233 boolean found = false; 234 for (Access declException: decl.getExceptionList()) { 235 if (e.type().instanceOf(declException.type())) { 236 found = true; 237 break; 238 } 239 } 240 if (!found) { 241 target.errorf("%s in %s may not throw more checked exceptions than" 242 + " overridden method %s in %s", 243 m.fullSignature(), m.hostType().typeName(), decl.fullSignature(), 244 decl.hostType().typeName()); 245 } 246 } 247 } 248 249 // 8.4.6.3 250 if (decl.isPublic() && !m.isPublic()) { 251 target.error("overriding access modifier error"); 252 } 253 // 8.4.6.3 254 if (decl.isProtected() && !(m.isPublic() || m.isProtected())) { 255 target.error("overriding access modifier error"); 256 } 257 // 8.4.6.3 258 if ((!decl.isPrivate() && !decl.isProtected() && !decl.isPublic()) && m.isPrivate()) { 259 target.error("overriding access modifier error"); 260 } 261 // regardless of overriding 262 if (decl.isFinal()) { 263 target.errorf("method %s in %s can not override final method %s in %s", 264 m.fullSignature(), hostType().typeName(), decl.fullSignature(), 265 decl.hostType().typeName()); 266 } 267 } 268 if (m.hides(decl)) { 269 // 8.4.6.2 270 if (m.isStatic() && !decl.isStatic()) { 271 target.error("a static method may not hide an instance method"); 272 } 273 // 8.4.6.3 274 if (!m.mayOverride(decl)) { 275 target.error("can not hide a method with a different return type"); 276 } 277 // 8.4.4 278 for (int i = 0; i < m.getNumException(); i++) { 279 Access e = m.getException(i); 280 boolean found = false; 281 for (int j = 0; !found && j < decl.getNumException(); j++) { 282 if (e.type().instanceOf(decl.getException(j).type())) { 283 found = true; 284 } 285 } 286 if (!found) { 287 target.error("may not throw more checked exceptions than hidden method"); 288 } 289 } 290 // 8.4.6.3 291 if (decl.isPublic() && !m.isPublic()) { 292 target.errorf("hiding access modifier error:" 293 + " public method %s in %s is hidden by non public method %s in %s", 294 decl.fullSignature(), decl.hostType().typeName(), m.fullSignature(), 295 m.hostType().typeName()); 296 } 297 // 8.4.6.3 298 if (decl.isProtected() && !(m.isPublic() || m.isProtected())) { 299 target.errorf("hiding access modifier error:" 300 + " protected method %s in %s is hidden by non (public|protected) method %s in %s", 301 decl.fullSignature(), decl.hostType().typeName(), m.fullSignature(), 302 m.hostType().typeName()); 303 } 304 // 8.4.6.3 305 if ((!decl.isPrivate() && !decl.isProtected() && !decl.isPublic()) && m.isPrivate()) { 306 target.errorf("hiding access modifier error:" 307 + " default method %s in %s is hidden by private method %s in %s", 308 decl.fullSignature(), decl.hostType().typeName(), m.fullSignature(), 309 m.hostType().typeName()); 310 } 311 if (decl.isFinal()) { 312 target.errorf("method %s in %s can not hide final method %s in %s", 313 m.fullSignature(), hostType().typeName(), decl.fullSignature(), 314 decl.hostType().typeName()); 315 } 316 } 317 } 318 } 319 } 320 321 public void ClassDecl.typeCheck() { 322 super.typeCheck(); 323 324 // check if methods from superclass are incompatible with interface methods 325 for (Iterator<MethodDecl> iter = interfacesMethodsIterator(); iter.hasNext(); ) { 326 MethodDecl decl = iter.next(); 327 SimpleSet set = localMethodsSignature(decl.signature()); 328 Iterator i2 = set.iterator(); 329 boolean overridden = false; 330 while (i2.hasNext()) { 331 MethodDecl m = (MethodDecl) i2.next(); 332 // regardless of overriding 333 // 8.4.6.3 334 if (m.overrideCandidate(decl)) { 335 overridden = true; 336 if (!m.mayOverride(decl)) { 337 errorf("the return type of method %s in %s does not match the return type of" 338 + " method %s in %s and may thus not be overridden", 339 m.fullSignature(), m.hostType().typeName(), decl.fullSignature(), 340 decl.hostType().typeName()); 341 } 342 } 343 } 344 if (!overridden) { 345 set = ancestorMethods(decl.signature()); 346 for (i2 = set.iterator(); i2.hasNext(); ) { 347 MethodDecl m = (MethodDecl) i2.next(); 348 if (!m.isAbstract() && m.overrideCandidate(decl) && 349 !m.mayOverride(decl)) { 350 errorf("the return type of method %s in %s does not match the return type of" 351 + " method %s in %s and may thus not be overridden", 352 m.fullSignature(), m.hostType().typeName(), decl.fullSignature(), 353 decl.hostType().typeName()); 354 } 355 } 356 } 357 } 358 } 359 360 syn boolean MethodDecl.mayOverride(MethodDecl m) = type() == m.type(); 361 362 public void ClassDecl.nameCheck() { 363 super.nameCheck(); 364 if (hasSuperClass() && !getSuperClass().type().isClassDecl()) { 365 errorf("a class may only inherit another class, which %s is not", 366 getSuperClass().type().typeName()); 367 } 368 if (isObject() && hasSuperClass()) { 369 error("class Object may not have a superclass"); 370 } 371 if (isObject() && getNumImplements() != 0) { 372 error("class Object may not implement an interface"); 373 } 374 375 // 8.1.3 376 if (isCircular()) { 377 errorf("circular inheritance dependency in %s", typeName()); 378 } 379 380 // 8.1.4 381 HashSet set = new HashSet(); 382 for (int i = 0; i < getNumImplements(); i++) { 383 TypeDecl decl = getImplements(i).type(); 384 if (!decl.isInterfaceDecl() && !decl.isUnknown()) { 385 errorf("type %s can not implement the non-interface type %s", fullName(), decl.fullName()); 386 } 387 if (set.contains(decl)) { 388 errorf("type %s is mentionened multiple times in implements clause", decl.fullName()); 389 } 390 set.add(decl); 391 } 392 393 for (Iterator iter = interfacesMethodsIterator(); iter.hasNext(); ) { 394 MethodDecl m = (MethodDecl) iter.next(); 395 if (localMethodsSignature(m.signature()).isEmpty()) { 396 SimpleSet s = superclass().methodsSignature(m.signature()); 397 for (Iterator i2 = s.iterator(); i2.hasNext(); ) { 398 MethodDecl n = (MethodDecl) i2.next(); 399 if (n.accessibleFrom(this)) { 400 interfaceMethodCompatibleWithInherited(m, n); 401 } 402 } 403 if (s.isEmpty()) { 404 for (Iterator i2 = interfacesMethodsSignature(m.signature()).iterator(); i2.hasNext(); ) { 405 MethodDecl n = (MethodDecl) i2.next(); 406 // TODO don't report error twice 407 checkAbstractMethodDecls(m, n); 408 } 409 } 410 } 411 } 412 } 413 414 /** 415 * Check compatibility of interface method and superclass method. 416 * @param m interface method 417 * @param n superclass method 418 */ 419 private void ClassDecl.interfaceMethodCompatibleWithInherited(MethodDecl m, MethodDecl n) { 420 if (n.isAbstract()) { 421 checkAbstractMethodDecls(m, n); 422 } 423 if (n.isStatic()) { 424 error("Xa static method may not hide an instance method"); 425 } 426 if (!n.isAbstract() && !n.isPublic()) { 427 errorf("Xoverriding access modifier error for %s in %s and %s", 428 m.fullSignature(), m.hostType().typeName(), n.hostType().typeName()); 429 } 430 if (!n.mayOverride(m) && !m.mayOverride(m)) { 431 errorf("Xthe return type of method %s in %s does not match" 432 + " the return type of method %s in %s and may thus not be overridden", 433 m.fullSignature(), m.hostType().typeName(), n.fullSignature(), n.hostType().typeName()); 434 } 435 436 if (!n.isAbstract()) { 437 // n implements and overrides method m in the interface 438 // may not throw more checked exceptions 439 for (Access e: n.getExceptionList()) { 440 if (e.type().isCheckedException()) { 441 boolean found = false; 442 for (Access declException: m.getExceptionList()) { 443 if (e.type().instanceOf(declException.type())) { 444 found = true; 445 break; 446 } 447 } 448 if (!found) { 449 errorf("%s in %s may not throw more checked exceptions than overridden method %s in %s", 450 n.fullSignature(), n.hostType().typeName(), m.fullSignature(), 451 m.hostType().typeName()); 452 } 453 } 454 } 455 } 456 } 457 458 public void InterfaceDecl.nameCheck() { 459 super.nameCheck(); 460 if (isCircular()) { 461 errorf("circular inheritance dependency in %s", typeName()); 462 } else { 463 for (int i = 0; i < getNumSuperInterface(); i++) { 464 TypeDecl typeDecl = getSuperInterface(i).type(); 465 if (typeDecl.isCircular()) { 466 errorf("circular inheritance dependency in %s", typeName()); 467 } 468 } 469 } 470 for (Iterator<SimpleSet> iter = methodsSignatureMap().values().iterator(); iter.hasNext(); ) { 471 SimpleSet set = iter.next(); 472 if (set.size() > 1) { 473 Iterator i2 = set.iterator(); 474 MethodDecl m = (MethodDecl) i2.next(); 475 while (i2.hasNext()) { 476 MethodDecl n = (MethodDecl) i2.next(); 477 checkAbstractMethodDecls(m, n); 478 } 479 } 480 } 481 } 482 483 /** 484 * Error-check two interface method declarations. 485 */ 486 protected void TypeDecl.checkAbstractMethodDecls(MethodDecl m1, MethodDecl m2) { 487 if (!m1.mayOverride(m2) && !m2.mayOverride(m1)) { 488 StringBuilder err = new StringBuilder(); 489 TypeDecl host1 = m1.hostType(); 490 TypeDecl host2 = m2.hostType(); 491 if (host1 != this || host2 != this) { 492 err.append("inherited "); 493 } 494 err.append("method " + m1.fullSignature() + " is multiply declared " 495 + "with incompatible return types in " + fullName()); 496 error(err.toString()); 497 } 498 } 499 } 500 501