001    /* Copyright (c) 2005-2008, Torbjorn Ekman
002     *                    2013, Jesper Öqvist <jesper.oqvist@cs.lth.se>
003     * All rights reserved.
004     *
005     * Redistribution and use in source and binary forms, with or without
006     * modification, are permitted provided that the following conditions are met:
007     *
008     * 1. Redistributions of source code must retain the above copyright notice,
009     * this list of conditions and the following disclaimer.
010     *
011     * 2. Redistributions in binary form must reproduce the above copyright notice,
012     * this list of conditions and the following disclaimer in the documentation
013     * and/or other materials provided with the distribution.
014     *
015     * 3. Neither the name of the copyright holder nor the names of its
016     * contributors may be used to endorse or promote products derived from this
017     * software without specific prior written permission.
018     *
019     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
020     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
021     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
023     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
024     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
025     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
026     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
027     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
028     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
029     * POSSIBILITY OF SUCH DAMAGE.
030     */
031    
032    aspect NameCheck {
033      public void ASTNode.nameCheck() {
034      }
035    
036      public TypeDecl ASTNode.extractSingleType(SimpleSet c) {
037        if (c.size() != 1) {
038          return null;
039        }
040        return (TypeDecl) c.iterator().next();
041      }
042    
043      public void SingleTypeImportDecl.nameCheck() {
044        if (!getAccess().type().typeName().equals(typeName()) && !getAccess().type().isUnknown()) {
045          errorf("Single-type import %s is not the canonical name of type %s",
046              typeName(), getAccess().type().typeName());
047        } else if (allImportedTypes(getAccess().type().name()).size() > 1) {
048          errorf("%s is imported multiple times", getAccess().type().name());
049        }
050      }
051    
052      inh SimpleSet ImportDecl.allImportedTypes(String name);
053      eq CompilationUnit.getImportDecl().allImportedTypes(String name) = importedTypes(name);
054    
055      public void TypeImportOnDemandDecl.nameCheck() {
056        if (getAccess().lastAccess().isTypeAccess()
057            && !getAccess().type().typeName().equals(typeName())) {
058          errorf("On demand type import %s.* is not the canonical name of type %s",
059              typeName(), getAccess().type().typeName());
060        }
061      }
062    
063      public void CompilationUnit.nameCheck() {
064        for (int i = 0; i < getNumImportDecl(); i++) {
065          ImportDecl decl = getImportDecl(i);
066          if (!decl.isOnDemand()) {
067            Iterator importIter = decl.importedTypes().iterator();
068            while (importIter.hasNext()) {
069              TypeDecl importedType = (TypeDecl) importIter.next();
070              Iterator iter = localLookupType(importedType.name()).iterator();
071              while (iter.hasNext()) {
072                TypeDecl local = (TypeDecl) iter.next();
073                if (local != importedType) {
074                  errorf("imported type %s conflicts with visible type", decl.typeName());
075                }
076              }
077            }
078          }
079        }
080      }
081    
082      public void PackageAccess.nameCheck() {
083        if (!hasPackage(packageName())) {
084          errorf("package %s not found", packageName());
085        }
086      }
087    
088      syn boolean MethodAccess.validArgs() {
089        for (int i = 0; i < getNumArg(); i++) {
090          if (getArg(i).type().isUnknown()) {
091            return false;
092          }
093        }
094        return true;
095      }
096    
097      public void ConstructorDecl.nameCheck() {
098        super.nameCheck();
099        // 8.8
100        if (!hostType().name().equals(name())) {
101          errorf("constructor %s does not have the same name as the simple name of the host class %s",
102              name(), hostType().name());
103        }
104    
105        // 8.8.2
106        if (hostType().lookupConstructor(this) != this) {
107          errorf("constructor with signature %s is multiply declared in type %s", signature(),
108              hostType().typeName());
109        }
110    
111        if (circularThisInvocation(this)) {
112          errorf("The constructor %s may not directly or indirectly invoke itself", signature());
113        }
114      }
115    
116      // 8.8.5
117      syn lazy boolean ConstructorDecl.circularThisInvocation(ConstructorDecl decl) circular [true] {
118        if (hasConstructorInvocation()) {
119          Expr e = ((ExprStmt) getConstructorInvocation()).getExpr();
120          if (e instanceof ConstructorAccess) {
121            ConstructorDecl constructorDecl = ((ConstructorAccess) e).decl();
122            if (constructorDecl == decl) {
123              return true;
124            }
125            return constructorDecl.circularThisInvocation(decl);
126          }
127        }
128        return false;
129      }
130    
131      public void MethodDecl.nameCheck() {
132        // 8.4
133        // 8.4.2
134        if (hostType().methodsSignature(signature()).size() > 1) {
135          errorf("method with signature %s is multiply declared in type %s", signature(),
136              hostType().typeName());
137        }
138        // 8.4.3.4
139        if (isNative() && hasBlock()) {
140          error("native methods must have an empty semicolon body");
141        }
142        // 8.4.5
143        if (isAbstract() && hasBlock()) {
144          error("abstract methods must have an empty semicolon body");
145        }
146        // 8.4.5
147        if (!hasBlock() && !(isNative() || isAbstract())) {
148          error("only abstract and native methods may have an empty semicolon body");
149        }
150      }
151    
152      public void ConstructorAccess.nameCheck() {
153        super.nameCheck();
154        ConstructorDecl decl = (ConstructorDecl) enclosingBodyDecl();
155        if (((ExprStmt) decl.getConstructorInvocation()).getExpr() == this) {
156          // don't error-check the parsed constructor invocation in case it is not the used one
157          if (decls().isEmpty()) {
158            errorf("no constructor matches %s", this.prettyPrint());
159          } else if (decls().size() > 1 && validArgs()) {
160            errorf("several most specific constructors for %s", this.prettyPrint());
161            for (Iterator iter = decls().iterator(); iter.hasNext(); ) {
162              errorf("         %s", ((ConstructorDecl) iter.next()).signature());
163            }
164          }
165        }
166      }
167    
168      syn boolean ConstructorAccess.validArgs() {
169        for (int i = 0; i < getNumArg(); i++) {
170          if (getArg(i).type().isUnknown()) {
171            return false;
172          }
173        }
174        return true;
175      }
176    
177      syn boolean ClassInstanceExpr.validArgs() {
178        for (int i = 0; i < getNumArg(); i++) {
179          if (getArg(i).type().isUnknown()) {
180            return false;
181          }
182        }
183        return true;
184      }
185    
186      public void ClassInstanceExpr.nameCheck() {
187        super.nameCheck();
188        if (decls().isEmpty()) {
189          errorf("can not instantiate %s no matching constructor found in %s", type().typeName(),
190              type().typeName());
191        } else if (decls().size() > 1 && validArgs()) {
192          error("several most specific constructors found");
193          for (Iterator iter = decls().iterator(); iter.hasNext(); ) {
194            errorf("         %s", ((ConstructorDecl) iter.next()).signature());
195          }
196        } else if (!hasTypeDecl()) {
197          // check if the constructor is accessible (stricter when not in a class instance expression)
198          // if constructor is private it can not be accessed outside the host class or a subtype of it
199          ConstructorDecl decl = decl();
200          if (decl.isProtected() && !hostPackage().equals(decl.hostPackage()) &&
201              !hostType().instanceOf(decl.hostType())) {
202            errorf("can not access the constructor %s", this.prettyPrint());
203          }
204        }
205      }
206    
207      public void ArrayTypeAccess.nameCheck() {
208        if (decl().elementType().isUnknown()) {
209          errorf("no type named %s", decl().elementType().typeName());
210        }
211      }
212    
213      public void TypeAccess.nameCheck() {
214        if (isQualified() && !qualifier().isTypeAccess() && !qualifier().isPackageAccess()) {
215          errorf("can not access the type named %s in this context", decl().typeName());
216        }
217        if (decls().isEmpty()) {
218          errorf("no visible type named %s", typeName());
219        }
220        if (decls().size() > 1) {
221          StringBuilder sb = new StringBuilder();
222          sb.append("several types named " + name() + ":");
223          for (Iterator iter = decls().iterator(); iter.hasNext(); ) {
224            TypeDecl t = (TypeDecl) iter.next();
225            sb.append(" " + t.typeName());
226          }
227          error(sb.toString());
228        }
229      }
230    
231    
232      public void ClassAccess.nameCheck() {
233        if (isQualified() && !qualifier().isTypeAccess()) {
234          error("class literal may only contain type names");
235        }
236      }
237    
238      public void VarAccess.nameCheck() {
239        if (decls().isEmpty() && (!isQualified() || !qualifier().type().isUnknown() || qualifier().isPackageAccess())) {
240          errorf("no field named %s is accessible", name());
241        }
242        if (decls().size() > 1) {
243          StringBuffer sb = new StringBuffer();
244          sb.append("several fields named " + name());
245          for (Iterator iter = decls().iterator(); iter.hasNext(); ) {
246            Variable v = (Variable) iter.next();
247            sb.append("\n    " + v.type().typeName() + "." + v.name() + " declared in " + v.hostType().typeName());
248          }
249          error(sb.toString());
250        }
251    
252        // 8.8.5.1
253        if (inExplicitConstructorInvocation() && !isQualified() && decl().isInstanceVariable() && hostType() == decl().hostType()) {
254          errorf("instance variable %s may not be accessed in an explicit constructor invocation", name());
255        }
256    
257        Variable v = decl();
258        if (!v.isFinal() && !v.isClassVariable() && !v.isInstanceVariable() && v.hostType() != hostType()) {
259          error("A parameter/variable used but not declared in an inner class must be declared final");
260        }
261    
262        // 8.3.2.3
263        if ((decl().isInstanceVariable() || decl().isClassVariable()) && !isQualified()) {
264          if (hostType() != null && !hostType().declaredBeforeUse(decl(), this)) {
265            if (inSameInitializer() && !simpleAssignment() && inDeclaringClass()) {
266              BodyDecl b = closestBodyDecl(hostType());
267              errorf("variable %s is used in %s before it is declared", decl().name(), b.prettyPrint());
268            }
269          }
270        }
271    
272      }
273    
274      // find the bodydecl declared in t in which this construct is nested
275      public BodyDecl VarAccess.closestBodyDecl(TypeDecl t) {
276        ASTNode node = this;
277        while (!(node.getParent().getParent() instanceof Program) && node.getParent().getParent() != t) {
278          node = node.getParent();
279        }
280        if (node instanceof BodyDecl) {
281          return (BodyDecl) node;
282        }
283        return null;
284      }
285    
286      syn boolean VarAccess.inSameInitializer() {
287        BodyDecl b = closestBodyDecl(decl().hostType());
288        if (b == null) {
289          return false;
290        }
291        if (b instanceof FieldDeclaration && ((FieldDeclaration) b).isStatic() == decl().isStatic()) {
292          return true;
293        }
294        if (b instanceof InstanceInitializer && !decl().isStatic()) {
295          return true;
296        }
297        if (b instanceof StaticInitializer && decl().isStatic()) {
298          return true;
299        }
300        return false;
301      }
302    
303      syn boolean VarAccess.simpleAssignment() = isDest() && getParent() instanceof AssignSimpleExpr;
304    
305      syn boolean VarAccess.inDeclaringClass() = hostType() == decl().hostType();
306    
307      inh boolean TypeDecl.hasPackage(String packageName);
308      inh boolean PackageAccess.hasPackage(String packageName);
309    
310      inh ASTNode TypeDecl.enclosingBlock();
311      eq MethodDecl.getBlock().enclosingBlock() = this;
312      eq ConstructorDecl.getBlock().enclosingBlock() = this;
313      eq InstanceInitializer.getBlock().enclosingBlock() = this;
314      eq Program.getChild().enclosingBlock() = null;
315    
316      public void TypeDecl.nameCheck() {
317        if (isTopLevelType() && lookupType(packageName(), name()) != this) {
318          errorf("duplicate type %s in package %s", name(), packageName());
319        }
320    
321        if (!isTopLevelType() && !isAnonymous() && !isLocalClass() && extractSingleType(enclosingType().memberTypes(name())) != this) {
322          errorf("duplicate member type %s in type %s", name(), enclosingType().typeName());
323        }
324    
325        // 14.3
326        if (isLocalClass()) {
327          TypeDecl typeDecl = extractSingleType(lookupType(name()));
328          if (typeDecl != null && typeDecl != this && typeDecl.isLocalClass() && enclosingBlock() == typeDecl.enclosingBlock()) {
329            errorf("local class named %s may not be redeclared as a local class in the same block",
330                name());
331          }
332        }
333    
334        if (!packageName().equals("") && hasPackage(fullName())) {
335          errorf("type name conflicts with a package using the same name: %s", name());
336        }
337    
338        // 8.1 & 9.1
339        if (hasEnclosingTypeDecl(name())) {
340          error("type may not have the same simple name as an enclosing type declaration");
341        }
342      }
343    
344      syn boolean TypeDecl.hasEnclosingTypeDecl(String name) {
345        TypeDecl enclosingType = enclosingType();
346        if (enclosingType != null) {
347          return enclosingType.name().equals(name) || enclosingType.hasEnclosingTypeDecl(name);
348        }
349        return false;
350      }
351    
352      public void FieldDeclaration.nameCheck() {
353        super.nameCheck();
354        // 8.3
355        for (Iterator iter = hostType().memberFields(name()).iterator(); iter.hasNext(); ) {
356          Variable v = (Variable) iter.next();
357          if (v != this && v.hostType() == hostType()) {
358            errorf("field named %s is multiply declared in type %s", name(), hostType().typeName());
359          }
360        }
361    
362      }
363    
364      inh VariableScope ParameterDeclaration.outerScope();
365      inh VariableScope VariableDeclaration.outerScope();
366      eq BasicCatch.getParameter().outerScope() = this;
367      eq Block.getStmt().outerScope() = this;
368      eq TypeDecl.getChild().outerScope() = this;
369      eq ForStmt.getInitStmt().outerScope() = this;
370      eq ForStmt.getStmt().outerScope() = this;
371      eq Program.getChild().outerScope() {
372        throw new UnsupportedOperationException("outerScope() not defined");
373      }
374    
375      public void VariableDeclaration.nameCheck() {
376        SimpleSet decls = outerScope().lookupVariable(name());
377        for (Iterator iter = decls.iterator(); iter.hasNext(); ) {
378          Variable var = (Variable) iter.next();
379          if (var instanceof VariableDeclaration) {
380            VariableDeclaration decl = (VariableDeclaration) var;
381            if (decl != this && decl.enclosingBodyDecl() == enclosingBodyDecl()) {
382              errorf("duplicate declaration of local variable %s in enclosing scope", name());
383            }
384          }
385          // 8.4.1
386          else if (var instanceof ParameterDeclaration) {
387            ParameterDeclaration decl = (ParameterDeclaration) var;
388            if (decl.enclosingBodyDecl() == enclosingBodyDecl()) {
389              errorf("duplicate declaration of local variable and parameter %s", name());
390            }
391          }
392        }
393        if (getParent().getParent() instanceof Block) {
394          Block block = (Block) getParent().getParent();
395          for (int i = 0; i < block.getNumStmt(); i++) {
396            if (block.getStmt(i) instanceof Variable) {
397              Variable v = (Variable) block.getStmt(i);
398              if (v.name().equals(name()) && v != this) {
399                errorf("duplicate declaration of local variable %s", name());
400              }
401            }
402          }
403        }
404      }
405    
406      public void ParameterDeclaration.nameCheck() {
407        SimpleSet decls = outerScope().lookupVariable(name());
408        for (Iterator iter = decls.iterator(); iter.hasNext(); ) {
409          Variable var = (Variable) iter.next();
410          if (var instanceof VariableDeclaration) {
411            VariableDeclaration decl = (VariableDeclaration) var;
412            if (decl.enclosingBodyDecl() == enclosingBodyDecl()) {
413              errorf("duplicate declaration of local variable %s", name());
414            }
415          } else if (var instanceof ParameterDeclaration) {
416            ParameterDeclaration decl = (ParameterDeclaration) var;
417            if (decl.enclosingBodyDecl() == enclosingBodyDecl()) {
418              errorf("duplicate declaration of local variable %s", name());
419            }
420          }
421        }
422    
423        // 8.4.1
424        if (!lookupVariable(name()).contains(this)) {
425          errorf("duplicate declaration of parameter %s", name());
426        }
427      }
428    
429      inh BodyDecl ParameterDeclaration.enclosingBodyDecl();
430    
431      public void LabeledStmt.nameCheck() {
432        LabeledStmt stmt = lookupLabel(getLabel());
433        if (stmt != null) {
434          if (stmt.enclosingBodyDecl() == enclosingBodyDecl()) {
435            error("Labels can not shadow labels in the same member");
436          }
437        }
438      }
439    
440      inh boolean BreakStmt.insideLoop();
441      inh boolean ContinueStmt.insideLoop();
442    
443      eq Program.getChild().insideLoop() = false;
444      eq TypeDecl.getBodyDecl(int i).insideLoop() = false;
445      eq ForStmt.getStmt().insideLoop() = true;
446      eq WhileStmt.getStmt().insideLoop() = true;
447      eq DoStmt.getStmt().insideLoop() = true;
448    
449      inh boolean BreakStmt.insideSwitch();
450      eq Program.getChild().insideSwitch() = false;
451      eq TypeDecl.getBodyDecl(int i).insideSwitch() = false;
452      eq SwitchStmt.getBlock().insideSwitch() = true;
453    
454      public void BreakStmt.nameCheck() {
455        if (!hasLabel() && !insideLoop() && !insideSwitch()) {
456          error("break outside switch or loop");
457        } else if (hasLabel()) {
458          LabeledStmt label = lookupLabel(getLabel());
459          if (label == null) {
460            error("labeled break must have visible matching label");
461          }
462        }
463      }
464    
465      public void ContinueStmt.nameCheck() {
466        if (!insideLoop()) {
467          error("continue outside loop");
468        } else if (hasLabel()) {
469          LabeledStmt label = lookupLabel(getLabel());
470          if (label == null) {
471            error("labeled continue must have visible matching label");
472          } else if (!label.getStmt().continueLabel()) {
473            errorf("%s is not a loop label", getLabel());
474          }
475        }
476      }
477    
478      syn boolean Stmt.continueLabel() = false;
479      eq ForStmt.continueLabel() = true;
480      eq WhileStmt.continueLabel() = true;
481      eq DoStmt.continueLabel() = true;
482    
483      public void ConstCase.nameCheck() {
484        if (getValue().isConstant() && bind(this) != this) {
485          errorf("constant expression %s is multiply declared in two case statements",
486              getValue().prettyPrint());
487        }
488      }
489    
490      public void DefaultCase.nameCheck() {
491        if (bind(this) != this) {
492          error("only one default case statement allowed");
493        }
494      }
495    
496      inh lazy Case Case.bind(Case c);
497      eq SwitchStmt.getBlock().bind(Case c) {
498        Block b = getBlock();
499        for (int i = 0; i < b.getNumStmt(); i++) {
500          if (b.getStmt(i) instanceof Case && ((Case) b.getStmt(i)).constValue(c)) {
501            return (Case) b.getStmt(i);
502          }
503        }
504        return null;
505      }
506      eq Program.getChild().bind(Case c) = null;
507    
508      syn boolean TypeDecl.assignableToInt() = false;
509      eq IntegralType.assignableToInt() = true;
510      eq LongType.assignableToInt() = false;
511    
512      syn boolean Case.constValue(Case c);
513      eq ConstCase.constValue(Case c) {
514        if (!(c instanceof ConstCase) || !getValue().isConstant()) {
515          return false;
516        }
517        if (!getValue().type().assignableToInt() || !((ConstCase) c).getValue().type().assignableToInt()) {
518          return false;
519        }
520        return getValue().constant().intValue() == ((ConstCase) c).getValue().constant().intValue();
521      }
522      eq DefaultCase.constValue(Case c) = c instanceof DefaultCase;
523    
524      inh SimpleSet LocalClassDeclStmt.otherLocalClassDecls(String name);
525      inh SimpleSet Block.otherLocalClassDecls(String name);
526    
527      eq BodyDecl.getChild().otherLocalClassDecls(String name) = SimpleSet.emptySet;
528    
529      eq Block.getStmt(int index).otherLocalClassDecls(String name) {
530        SimpleSet local = SimpleSet.emptySet;
531        for (int i = index-1; i >= 0 && !(getStmt(i) instanceof Case); --i) {
532          if (getStmt(i) instanceof LocalClassDeclStmt) {
533            TypeDecl t = ((LocalClassDeclStmt) getStmt(i)).getClassDecl();
534            if (t.name().equals(name)) {
535              local = local.add(t);
536            }
537          }
538        }
539        if (!local.isEmpty()) {
540          return local;
541        } else {
542          return otherLocalClassDecls(name);
543        }
544      }
545    
546      /**
547       * Checks that this local class declaration does not conflict with a previous
548       * declaration in the local scope.
549       */
550      public void LocalClassDeclStmt.nameCheck() {
551        TypeDecl decl = getClassDecl();
552        SimpleSet decls = otherLocalClassDecls(decl.name());
553        if (!decls.isEmpty()) {
554          errorf("another local class %s has already been declared in this scope", decl.name());
555        }
556      }
557    }