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