001    /* Copyright (c) 2011, Jesper Öqvist <jesper.oqvist@cs.lth.se>
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 PreciseRethrow {
032    
033      syn Collection<TypeDecl> Expr.throwTypes() {
034        Collection<TypeDecl> tts = new LinkedList<TypeDecl>();
035        tts.add(type());
036        return tts;
037      }
038    
039      eq VarAccess.throwTypes() = decl().throwTypes();
040    
041      syn lazy Collection<TypeDecl> VariableDeclaration.throwTypes() {
042        Collection<TypeDecl> tts = new LinkedList<TypeDecl>();
043        tts.add(type());
044        return tts;
045      }
046    
047      syn lazy Collection<TypeDecl> FieldDeclaration.throwTypes() {
048        Collection<TypeDecl> tts = new LinkedList<TypeDecl>();
049        tts.add(type());
050        return tts;
051      }
052    
053      syn lazy Collection<TypeDecl> ParameterDeclaration.throwTypes() {
054        if (isCatchParam() && isEffectivelyFinal()) {
055          // The catch parameter must be final to refine the throw type.
056          return catchClause().caughtExceptions();
057        } else {
058          return Collections.singleton(type());
059        }
060      }
061    
062      syn lazy Collection<TypeDecl> CatchParameterDeclaration.throwTypes() =
063          catchClause().caughtExceptions();
064    
065      /**
066       * Note: this attribute deviates from what the JLS says about "effectively final",
067       * simply because the attribute name would be too confusing if it did not return true
068       * when the variable was explicitly declared final. The JLS considers declared final
069       * and effectively final to be mutually exclusive, we don't.
070       */
071      syn boolean ParameterDeclaration.isEffectivelyFinal() = isFinal() || !inhModifiedInScope(this);
072    
073      /**
074       * @return true if the variable var is modified in the local scope
075       */
076      inh boolean ParameterDeclaration.inhModifiedInScope(Variable var);
077      eq ConstructorDecl.getParameter().inhModifiedInScope(Variable var) =
078          getBlock().modifiedInScope(var);
079      eq MethodDecl.getParameter().inhModifiedInScope(Variable var) =
080          hasBlock() && getBlock().modifiedInScope(var);
081      eq BasicCatch.getParameter().inhModifiedInScope(Variable var) =
082          getBlock().modifiedInScope(var);
083    
084      syn boolean Stmt.modifiedInScope(Variable var);
085      eq VarDeclStmt.modifiedInScope(Variable var) {
086        for (VariableDeclaration decl: getSingleDeclList()) {
087          if (decl.modifiedInScope(var)) {
088            return true;
089          }
090        }
091        return false;
092      }
093      syn boolean VariableDeclaration.modifiedInScope(Variable var) =
094          hasInit() && getInit().modifiedInScope(var);
095      eq EmptyStmt.modifiedInScope(Variable var) = false;
096      eq LabeledStmt.modifiedInScope(Variable var) = getStmt().modifiedInScope(var);
097      eq Block.modifiedInScope(Variable var) {
098        for (Stmt stmt : getStmtList()) {
099          if (stmt.modifiedInScope(var)) {
100            return true;
101          }
102        }
103        return false;
104      }
105      eq SwitchStmt.modifiedInScope(Variable var) = getBlock().modifiedInScope(var);
106      eq Case.modifiedInScope(Variable var) = false;
107      eq BreakStmt.modifiedInScope(Variable var) = false;
108      eq ContinueStmt.modifiedInScope(Variable var) = false;
109      eq ReturnStmt.modifiedInScope(Variable var) = false;
110      eq ThrowStmt.modifiedInScope(Variable var) = false;
111      eq IfStmt.modifiedInScope(Variable var) {
112        if (getThen().modifiedInScope(var)) {
113          return true;
114        }
115        return hasElse() && getElse().modifiedInScope(var);
116      }
117      eq WhileStmt.modifiedInScope(Variable var) = getStmt().modifiedInScope(var);
118      eq DoStmt.modifiedInScope(Variable var) = getStmt().modifiedInScope(var);
119      eq ForStmt.modifiedInScope(Variable var) {
120        for (Stmt stmt : getInitStmtList()) {
121          if (stmt.modifiedInScope(var)) {
122            return true;
123          }
124        }
125        for (Stmt stmt : getUpdateStmtList()) {
126          if (stmt.modifiedInScope(var)) {
127            return true;
128          }
129        }
130        return getStmt().modifiedInScope(var);
131      }
132      eq EnhancedForStmt.modifiedInScope(Variable var) = getStmt().modifiedInScope(var);
133      eq SynchronizedStmt.modifiedInScope(Variable var) = getBlock().modifiedInScope(var);
134      eq TryStmt.modifiedInScope(Variable var) {
135        if (getBlock().modifiedInScope(var)) {
136          return true;
137        }
138        for (CatchClause cc : getCatchClauseList()) {
139          if (cc.modifiedInScope(var)) {
140            return true;
141          }
142        }
143        return hasNonEmptyFinally() && getFinally().modifiedInScope(var);
144      }
145      eq AssertStmt.modifiedInScope(Variable var) = false;
146      eq LocalClassDeclStmt.modifiedInScope(Variable var) = false;
147    
148      syn boolean CatchClause.modifiedInScope(Variable var) = getBlock().modifiedInScope(var);
149      syn boolean Expr.modifiedInScope(Variable var) = false;
150      eq AbstractDot.modifiedInScope(Variable var) = getLeft().modifiedInScope(var);
151      eq VarAccess.modifiedInScope(Variable var) = false;
152      eq AssignExpr.modifiedInScope(Variable var) =
153          getDest().isVariable(var) || getSource().modifiedInScope(var);
154      eq ExprStmt.modifiedInScope(Variable var) = getExpr().modifiedInScope(var);
155      eq Binary.modifiedInScope(Variable var) =
156          getLeftOperand().modifiedInScope(var) || getRightOperand().modifiedInScope(var);
157      eq Unary.modifiedInScope(Variable var) = getOperand().modifiedInScope(var);
158      eq ParExpr.modifiedInScope(Variable var) = getExpr().modifiedInScope(var);
159      eq CastExpr.modifiedInScope(Variable var) = getExpr().modifiedInScope(var);
160      eq InstanceOfExpr.modifiedInScope(Variable var) = getExpr().modifiedInScope(var);
161      eq ClassInstanceExpr.modifiedInScope(Variable var) {
162        for (int i = 0; i < getNumArg(); ++i) {
163          if (getArg(i).modifiedInScope(var)) {
164            return true;
165          }
166        }
167        return false;
168      }
169      eq MethodAccess.modifiedInScope(Variable var) {
170        for (int i = 0; i < getNumArg(); ++i) {
171          if (getArg(i).modifiedInScope(var)) {
172            return true;
173          }
174        }
175        return false;
176      }
177      eq ConstructorAccess.modifiedInScope(Variable var) {
178        for (int i = 0; i < getNumArg(); ++i) {
179          if (getArg(i).modifiedInScope(var)) {
180            return true;
181          }
182        }
183        return false;
184      }
185      eq ArrayInit.modifiedInScope(Variable var) {
186        for (int i = 0; i < getNumInit(); ++i) {
187          if (getInit(i).modifiedInScope(var)) {
188            return true;
189          }
190        }
191        return false;
192      }
193      eq ArrayCreationExpr.modifiedInScope(Variable var) =
194          hasArrayInit() && getArrayInit().modifiedInScope(var);
195      eq ConditionalExpr.modifiedInScope(Variable var) =
196          getCondition().modifiedInScope(var)
197          || getTrueExpr().modifiedInScope(var)
198          || getFalseExpr().modifiedInScope(var);
199    
200      syn boolean Expr.isVariable(Variable var) = false;
201      eq VarAccess.isVariable(Variable var) = decl() == var;
202    
203      /**
204       * @return true if this is the parameter declaration of a catch clause
205       */
206      inh boolean ParameterDeclaration.isCatchParam();
207      eq Program.getChild(int i).isCatchParam() = false;
208      eq ConstructorDecl.getParameter().isCatchParam() = false;
209      eq MethodDecl.getParameter().isCatchParam() = false;
210      eq BasicCatch.getParameter().isCatchParam() = true;
211    
212      inh CatchClause ParameterDeclaration.catchClause();
213      inh CatchClause CatchParameterDeclaration.catchClause();
214    
215      eq Program.getChild(int i).catchClause() {
216        throw new IllegalStateException("Could not find parent " + "catch clause");
217      }
218    
219      eq CatchClause.getChild(int i).catchClause() = this;
220    
221      inh Collection<TypeDecl> CatchClause.caughtExceptions();
222    
223      eq TryStmt.getCatchClause(int index).caughtExceptions() {
224        Collection<TypeDecl> excp = new HashSet<TypeDecl>();
225        getBlock().collectExceptions(excp, this);
226        Collection<TypeDecl> caught = new LinkedList<TypeDecl>();
227        Iterator<TypeDecl> iter = excp.iterator();
228        while (iter.hasNext()) {
229          TypeDecl exception = iter.next();
230          // this catch clause handles the exception
231          if (!getCatchClause(index).handles(exception)) {
232            continue;
233          }
234          // no previous catch clause handles the exception
235          boolean already = false;
236          for (int i = 0; i < index; ++i) {
237            if (getCatchClause(i).handles(exception)) {
238              already = true;
239              break;
240            }
241          }
242          if (!already) {
243            caught.add(exception);
244          }
245        }
246        return caught;
247      }
248    
249      refine ExceptionHandling
250      public void ThrowStmt.exceptionHandling() {
251        Collection<TypeDecl> exceptionTypes = getExpr().throwTypes();
252        for (TypeDecl exceptionType : exceptionTypes) {
253          if (exceptionType == typeNull()) {
254            exceptionType = typeNullPointerException();
255          }
256          // 8.4.4
257          if (exceptionType.isCheckedException() && !handlesException(exceptionType)) {
258            errorf("%s throws uncaught exception %s", this.prettyPrint(), exceptionType.fullName());
259          }
260        }
261      }
262    
263      refine ExceptionHandling
264      protected boolean ThrowStmt.reachedException(TypeDecl catchType) {
265        Collection<TypeDecl> exceptionTypes = getExpr().throwTypes();
266        boolean reached = false;
267        for (TypeDecl exceptionType : exceptionTypes) {
268          if (exceptionType == typeNull()) {
269            exceptionType = typeNullPointerException();
270          }
271          if (catchType.mayCatch(exceptionType)) {
272            reached = true;
273            break;
274          }
275          if (super.reachedException(catchType)) {
276            reached = true;
277            break;
278          }
279        }
280        return reached;
281      }
282    
283      inh boolean CatchClause.reportUnreachable();
284      eq CatchClause.getBlock().reportUnreachable() = false;
285    
286      void BasicCatch.checkUnreachableStmt() {
287        if (!getBlock().reachable() && reportUnreachable()) {
288          errorf("the exception %s is not thrown in the body of the try statement",
289              getParameter().type().fullName());
290        }
291      }
292    
293      void MultiCatch.checkUnreachableStmt() {
294        if (!getBlock().reachable() && reportUnreachable()) {
295          errorf("the exception %s is not thrown in the body of the try statement",
296              getParameter().type().fullName());
297        }
298      }
299    }