001    /*
002     * The JastAdd Extensible Java Compiler (http://jastadd.org) is covered
003     * by the modified BSD License. You should have received a copy of the
004     * modified BSD license with this compiler.
005     * 
006     * Copyright (c) 2005-2008, Torbjorn Ekman
007     * All rights reserved.
008     */
009    
010    aspect NameCheck {
011      public void ASTNode.nameCheck() {
012      }
013    
014      public TypeDecl ASTNode.extractSingleType(SimpleSet c) {
015        if(c.size() != 1)
016          return null;
017        return (TypeDecl)c.iterator().next();
018      }
019    
020      public void SingleTypeImportDecl.nameCheck() {
021        if(!getAccess().type().typeName().equals(typeName()) && !getAccess().type().isUnknown())
022          error("Single-type import " + typeName() + " is not the canonical name of type " + getAccess().type().typeName());
023        else if(allImportedTypes(getAccess().type().name()).size() > 1)
024          error(getAccess().type().name() + " is imported multiple times");
025      }
026      inh SimpleSet SingleTypeImportDecl.allImportedTypes(String name);
027      eq CompilationUnit.getImportDecl().allImportedTypes(String name) =
028        importedTypes(name);
029    
030      public void TypeImportOnDemandDecl.nameCheck() {
031        if(getAccess().lastAccess().isTypeAccess() && !getAccess().type().typeName().equals(typeName()))
032          error("On demand type import " + typeName() + ".* is not the canonical name of type " + getAccess().type().typeName());
033      }
034    
035      public void CompilationUnit.nameCheck() {
036        for(int i = 0; i < getNumImportDecl(); i++) {
037          ImportDecl decl = getImportDecl(i);
038          if(decl instanceof SingleTypeImportDecl) {
039            TypeDecl importedType = decl.getAccess().type();
040            Iterator iter = localLookupType(importedType.name()).iterator();
041            while (iter.hasNext()) {
042              TypeDecl local = (TypeDecl) iter.next();
043              if (local != importedType)
044                error("imported type " + decl + " is conflicting with visible type");
045            }
046          }
047        }
048      }
049    
050      public void PackageAccess.nameCheck() {
051        if(!hasPackage(packageName())) {
052          error(packageName() + " not found");
053        }
054      }
055    
056      public void AmbiguousAccess.nameCheck() {
057        error("ambiguous name " + name());
058      }
059      
060      public void PackageOrTypeAccess.nameCheck() {
061        error("packageortype name " + name());
062      }
063    
064      
065      syn boolean MethodAccess.validArgs() {
066        for(int i = 0; i < getNumArg(); i++)
067          if(getArg(i).type().isUnknown())
068            return false;
069        return true;
070      }
071    
072    
073      public void ConstructorDecl.nameCheck() {
074        super.nameCheck();
075        // 8.8
076        if(!hostType().name().equals(name()))
077          error("constructor " + name() +" does not have the same name as the simple name of the host class " + hostType().name());
078        
079        // 8.8.2
080        if(hostType().lookupConstructor(this) != this)
081          error("constructor with signature " + signature() + " is multiply declared in type " + hostType().typeName());
082    
083        if(circularThisInvocation(this))
084          error("The constructor " + signature() + " may not directly or indirectly invoke itself");
085      }
086    
087      // 8.8.5
088      syn lazy boolean ConstructorDecl.circularThisInvocation(ConstructorDecl decl) circular [true] {
089        if(hasConstructorInvocation()) {
090          Expr e = ((ExprStmt)getConstructorInvocation()).getExpr();
091          if(e instanceof ConstructorAccess) {
092            ConstructorDecl constructorDecl = ((ConstructorAccess)e).decl();
093            if(constructorDecl == decl)
094              return true;
095            return constructorDecl.circularThisInvocation(decl);
096          }
097        }
098        return false;
099      }
100    
101      public void MethodDecl.nameCheck() {
102        // 8.4
103        // 8.4.2
104        if(!hostType().methodsSignature(signature()).contains(this))
105          error("method with signature " + signature() + " is multiply declared in type " + hostType().typeName());
106        // 8.4.3.4
107        if(isNative() && hasBlock())
108          error("native methods must have an empty semicolon body");
109        // 8.4.5
110        if(isAbstract() && hasBlock())
111          error("abstract methods must have an empty semicolon body");
112        // 8.4.5
113        if(!hasBlock() && !(isNative() || isAbstract()))
114          error("only abstract and native methods may have an empty semicolon body");
115      }
116    
117      public void ConstructorAccess.nameCheck() {
118        super.nameCheck();
119        if(decls().isEmpty())
120          error("no constructor named " + this);
121        if(decls().size() > 1 && validArgs()) {
122          error("several most specific constructors for " + this);
123          for(Iterator iter = decls().iterator(); iter.hasNext(); ) {
124            error("         " + ((ConstructorDecl)iter.next()).signature());
125          }
126        }
127      }
128    
129      syn boolean ConstructorAccess.validArgs() {
130        for(int i = 0; i < getNumArg(); i++)
131          if(getArg(i).type().isUnknown())
132            return false;
133        return true;
134      }
135      syn boolean ClassInstanceExpr.validArgs() {
136        for(int i = 0; i < getNumArg(); i++)
137          if(getArg(i).type().isUnknown())
138            return false;
139        return true;
140      }
141    
142      public void ClassInstanceExpr.nameCheck() {
143        super.nameCheck();
144        if(decls().isEmpty())
145          error("can not instantiate " + type().typeName() + " no matching constructor found in " + type().typeName());
146        else if(decls().size() > 1 && validArgs()) {
147          error("several most specific constructors found");
148          for(Iterator iter = decls().iterator(); iter.hasNext(); ) {
149            error("         " + ((ConstructorDecl)iter.next()).signature());
150          }
151        }
152      }
153        
154      
155      public void ArrayTypeAccess.nameCheck() {
156        if(decl().elementType().isUnknown())
157          error("no type named " + decl().elementType().typeName());
158      }
159      
160      public void TypeAccess.nameCheck() {
161        if(isQualified() && !qualifier().isTypeAccess() && !qualifier().isPackageAccess())
162          error("can not access the type named " + decl().typeName() + " in this context");
163        if(decls().isEmpty())
164          error("no visible type named " + typeName());
165        if(decls().size() > 1) {
166          StringBuffer s = new StringBuffer();
167          s.append("several types named " + name() + ":");
168          for(Iterator iter = decls().iterator(); iter.hasNext(); ) {
169            TypeDecl t = (TypeDecl)iter.next();
170            s.append(" " + t.typeName());
171          }
172          error(s.toString());
173        }
174      }
175    
176    
177      public void ClassAccess.nameCheck() {
178        if(isQualified() && !qualifier().isTypeAccess())
179          error("class literal may only contain type names");
180      }
181    
182      public void VarAccess.nameCheck() {
183        if(decls().isEmpty() && (!isQualified() || !qualifier().type().isUnknown() || qualifier().isPackageAccess()))
184          error("no field named " + name() + " is accessible");
185        if(decls().size() > 1) {
186          StringBuffer s = new StringBuffer();
187          s.append("several fields named " + name());
188          for(Iterator iter = decls().iterator(); iter.hasNext(); ) {
189            Variable v = (Variable)iter.next();
190            s.append("\n    " + v.type().typeName() + "." + v.name() + " declared in " + v.hostType().typeName());
191          }
192          error(s.toString());
193        }
194          
195        // 8.8.5.1
196        if(inExplicitConstructorInvocation() && !isQualified() && decl().isInstanceVariable() && hostType() == decl().hostType())
197          error("instance variable " + name() + " may not be accessed in an explicit constructor invocation");
198    
199        Variable v = decl();
200        if(!v.isFinal() && !v.isClassVariable() && !v.isInstanceVariable() && v.hostType() != hostType())
201          error("A parameter/variable used but not declared in an inner class must be declared final");
202    
203        // 8.3.2.3
204        if((decl().isInstanceVariable() || decl().isClassVariable()) && !isQualified()) {
205          if(hostType() != null && !hostType().declaredBeforeUse(decl(), this)) {
206            if(inSameInitializer() && !simpleAssignment() && inDeclaringClass()) {
207              BodyDecl b = closestBodyDecl(hostType());
208              error("variable " + decl().name() + " is used in " + b + " before it is declared");
209            }
210          }
211        }
212    
213      }
214    
215      // find the bodydecl declared in t in which this construct is nested
216      public BodyDecl VarAccess.closestBodyDecl(TypeDecl t) {
217        ASTNode node = this;
218        while(!(node.getParent().getParent() instanceof Program) && node.getParent().getParent() != t) {
219          node = node.getParent();
220        }
221        if(node instanceof BodyDecl)
222          return (BodyDecl)node;
223        return null;
224      }
225    
226      syn boolean VarAccess.inSameInitializer() {
227        BodyDecl b = closestBodyDecl(decl().hostType());
228        if(b == null) return false;
229        if(b instanceof FieldDeclaration && ((FieldDeclaration)b).isStatic() == decl().isStatic())
230          return true;
231        if(b instanceof InstanceInitializer && !decl().isStatic())
232          return true;
233        if(b instanceof StaticInitializer && decl().isStatic())
234          return true;
235        return false;
236      }
237    
238      syn boolean VarAccess.simpleAssignment() = isDest() && getParent() instanceof AssignSimpleExpr;
239    
240      syn boolean VarAccess.inDeclaringClass() = hostType() == decl().hostType();
241    
242      inh boolean TypeDecl.hasPackage(String packageName);
243      inh boolean PackageAccess.hasPackage(String packageName);
244    
245      inh ASTNode TypeDecl.enclosingBlock();
246      eq MethodDecl.getBlock().enclosingBlock() = this;
247      eq ConstructorDecl.getBlock().enclosingBlock() = this;
248      eq InstanceInitializer.getBlock().enclosingBlock() = this;
249      eq Program.getChild().enclosingBlock() = null;
250    
251      public void TypeDecl.nameCheck() {
252        if(isTopLevelType() && lookupType(packageName(), name()) != this)
253          error("duplicate type " + name() + " in package " + packageName());
254      
255        if(!isTopLevelType() && !isAnonymous() && !isLocalClass() && extractSingleType(enclosingType().memberTypes(name())) != this)
256          error("duplicate member type " + name() + " in type " + enclosingType().typeName());
257    
258        // 14.3
259        if(isLocalClass()) {
260          TypeDecl typeDecl = extractSingleType(lookupType(name()));
261          if(typeDecl != null && typeDecl != this && typeDecl.isLocalClass() && enclosingBlock() == typeDecl.enclosingBlock())
262            error("local class named " + name() + " may not be redeclared as a local class in the same block");
263        }
264    
265        if(!packageName().equals("") && hasPackage(fullName()))
266          error("type name conflicts with a package using the same name: " + name());
267        
268        // 8.1 & 9.1
269        if(hasEnclosingTypeDecl(name())) {
270          error("type may not have the same simple name as an enclosing type declaration");
271        }
272      }
273    
274      syn boolean TypeDecl.hasEnclosingTypeDecl(String name) {
275        TypeDecl enclosingType = enclosingType();
276        if(enclosingType != null) {
277          return enclosingType.name().equals(name) || enclosingType.hasEnclosingTypeDecl(name);
278        }
279        return false;
280      }
281    
282      public void FieldDeclaration.nameCheck() {
283        super.nameCheck();
284        // 8.3
285        for(Iterator iter = hostType().memberFields(name()).iterator(); iter.hasNext(); ) {
286          Variable v = (Variable)iter.next();
287          if(v != this && v.hostType() == hostType())
288            error("field named " + name() + " is multiply declared in type " + hostType().typeName());
289        }
290    
291      }
292    
293      inh VariableScope ParameterDeclaration.outerScope();
294      inh VariableScope VariableDeclaration.outerScope();
295      eq BasicCatch.getParameter().outerScope() = this;
296      eq Block.getStmt().outerScope() = this;
297      eq TypeDecl.getBodyDecl().outerScope() = this;
298      eq ForStmt.getInitStmt().outerScope() = this;
299      eq ForStmt.getStmt().outerScope() = this;
300      eq Program.getChild().outerScope() {
301        throw new UnsupportedOperationException("outerScope() not defined");
302      }
303    
304      public void VariableDeclaration.nameCheck() {
305        SimpleSet decls = outerScope().lookupVariable(name());
306        for(Iterator iter = decls.iterator(); iter.hasNext(); ) {
307          Variable var = (Variable)iter.next();
308          if(var instanceof VariableDeclaration) {
309            VariableDeclaration decl = (VariableDeclaration)var;
310            if(decl != this && decl.enclosingBodyDecl() == enclosingBodyDecl())
311              error("duplicate declaration of local variable " + name() + " in enclosing scope");
312          }
313          // 8.4.1
314          else if(var instanceof ParameterDeclaration) {
315            ParameterDeclaration decl = (ParameterDeclaration)var;
316            if(decl.enclosingBodyDecl() == enclosingBodyDecl())
317              error("duplicate declaration of local variable and parameter " + name());
318          }
319        }
320        if(getParent().getParent() instanceof Block) {
321          Block block = (Block)getParent().getParent();
322          for(int i = 0; i < block.getNumStmt(); i++) {
323            if(block.getStmt(i) instanceof Variable) {
324              Variable v = (Variable)block.getStmt(i);
325              if(v.name().equals(name()) && v != this) {
326                error("duplicate declaration of local variable " + name());
327              }
328            }
329          }
330        }
331      }
332      
333      public void ParameterDeclaration.nameCheck() {
334        SimpleSet decls = outerScope().lookupVariable(name());
335        for(Iterator iter = decls.iterator(); iter.hasNext(); ) {
336          Variable var = (Variable)iter.next();
337          if(var instanceof VariableDeclaration) {
338            VariableDeclaration decl = (VariableDeclaration)var;
339            if(decl.enclosingBodyDecl() == enclosingBodyDecl())
340              error("duplicate declaration of local variable " + name());
341          }
342          else if(var instanceof ParameterDeclaration) {
343            ParameterDeclaration decl = (ParameterDeclaration)var;
344            if(decl.enclosingBodyDecl() == enclosingBodyDecl())
345              error("duplicate declaration of local variable " + name());
346          }
347        }
348    
349        // 8.4.1  
350        if(!lookupVariable(name()).contains(this)) {
351          error("duplicate declaration of parameter " + name());
352        }
353      }
354      inh BodyDecl ParameterDeclaration.enclosingBodyDecl();
355      
356      public void LabeledStmt.nameCheck() {
357        LabeledStmt stmt = lookupLabel(getLabel());
358        if(stmt != null) {
359          if(stmt.enclosingBodyDecl() == enclosingBodyDecl()) {
360            error("Labels can not shadow labels in the same member");
361          }
362        }
363      }
364      
365      inh boolean BreakStmt.insideLoop();
366      inh boolean ContinueStmt.insideLoop();
367    
368      eq Program.getChild().insideLoop() = false;
369      eq TypeDecl.getBodyDecl(int i).insideLoop() = false;
370      eq ForStmt.getStmt().insideLoop() = true;
371      eq WhileStmt.getStmt().insideLoop() = true;
372      eq DoStmt.getStmt().insideLoop() = true;
373      
374      inh boolean BreakStmt.insideSwitch();
375      eq Program.getChild().insideSwitch() = false;
376      eq TypeDecl.getBodyDecl(int i).insideSwitch() = false;
377      eq SwitchStmt.getBlock().insideSwitch() = true;
378    
379      public void BreakStmt.nameCheck() {
380        if(!hasLabel() && !insideLoop() && !insideSwitch())
381          error("break outside switch or loop");
382        else if(hasLabel()) {
383          LabeledStmt label = lookupLabel(getLabel());
384          if(label == null)
385            error("labeled break must have visible matching label");
386        }
387      }
388      
389      public void ContinueStmt.nameCheck() {
390        if(!insideLoop())
391          error("continue outside loop");
392        else if(hasLabel()) {
393          LabeledStmt label = lookupLabel(getLabel());
394          if(label == null)
395            error("labeled continue must have visible matching label");
396          else if(!label.getStmt().continueLabel())
397            error(getLabel() + " is not a loop label");
398        }
399      }
400    
401      syn boolean Stmt.continueLabel() = false;
402      eq ForStmt.continueLabel() = true;
403      eq WhileStmt.continueLabel() = true;
404      eq DoStmt.continueLabel() = true;
405      
406      public void ConstCase.nameCheck() {
407        if(getValue().isConstant() && bind(this) != this) {
408          error("constant expression " + getValue() + " is multiply declared in two case statements");
409        }
410      }
411      public void DefaultCase.nameCheck() {
412        if(bind(this) != this) {
413          error("only one default case statement allowed");
414        }
415      }
416    
417      inh lazy Case Case.bind(Case c);
418      eq SwitchStmt.getBlock().bind(Case c) {
419        Block b = getBlock();
420        for(int i = 0; i < b.getNumStmt(); i++)
421          if(b.getStmt(i) instanceof Case && ((Case)b.getStmt(i)).constValue(c))
422            return (Case)b.getStmt(i);
423        return null;
424      }
425      eq Program.getChild().bind(Case c) = null;
426    
427      syn boolean TypeDecl.assignableToInt() = false;
428      eq IntegralType.assignableToInt() = true;
429      eq LongType.assignableToInt() = false;
430    
431      syn boolean Case.constValue(Case c);
432      eq ConstCase.constValue(Case c) {
433        if(!(c instanceof ConstCase) || !getValue().isConstant())
434          return false;
435        if(!getValue().type().assignableToInt() || !((ConstCase)c).getValue().type().assignableToInt())
436          return false;
437        return getValue().constant().intValue() == ((ConstCase)c).getValue().constant().intValue();
438      }
439      eq DefaultCase.constValue(Case c) = c instanceof DefaultCase;
440    }