001 /* Copyright 2000, 2001, Compaq Computer Corporation */ 002 003 package javafe.tc; 004 005 import javafe.ast.*; 006 import javafe.util.*; 007 008 /** 009 * This module is responsible for handling <code>CompilationUnit</code>-level type 010 * checks. 011 * 012 * <p> In practice, these are mostly checks that the set of import declarations is 013 * legal. </p> 014 */ 015 016 public class CheckCompilationUnit 017 { 018 /** 019 * A new field for CompilationUnits: iff it is non-null then we have already 020 * checked that CompilationUnit. 021 */ 022 //@ invariant checkedField != null; 023 //@ invariant checkedField.decorationType == \type(Boolean); 024 //@ spec_public 025 private static ASTDecoration checkedField = 026 new ASTDecoration("javafe.tc.CheckCompilationUnit.checked"); 027 028 /** 029 * Check a <code>CompilationUnit</code>. 030 * 031 * <p> If this method is called multiple times on the same 032 * <code>CompilationUnit</code>, it has no effect the second and later 033 * times. </p> 034 * 035 * <p> Precondition: <code>cu</code> must have already been loaded by 036 * <code>OutsideEnv</code>. </p> 037 * 038 * <p> Any resulting errors or warnings are reported via 039 * <code>ErrorSet</code>. </p> 040 */ 041 //@ requires cu != null; 042 public static void checkCompilationUnit(CompilationUnit cu) { 043 // Check any given CompilationUnit at most once: 044 if (checkedField.get(cu) != null) 045 return; 046 checkedField.set(cu, Boolean.TRUE); 047 048 Info.out("[checkCompilationUnit: checking for " 049 + Location.toFileName(cu.loc) + "]"); 050 051 052 // Warn about "file local" types declared public: 053 checkPublic(cu); 054 055 // Check to make sure that each import is individually well formed: 056 checkImports(cu); 057 058 // Don't declare two decls with same name in same package 059 // This is implemented by OutsideEnv -- mdl. 060 // Don't declare two types with the same name in the same file 061 // This is implemented in OutsideEnv -- mdl. 062 063 ImportDeclVec imports = cu.imports; 064 TypeDeclVec elems = cu.elems; 065 066 // Check for "import P.T" and "import Q.T" pairs. Such a pair * is legal iff 067 // P=Q. 068 069 // Moreover, check for "import P.T" where T is defined in this 070 // CompilationUnit. Such imports are legal iff P is the name of this 071 // package. 072 073 for (int i = 0; i < imports.size(); i++) { 074 // Skip to the next single-type import: 075 ImportDecl idecl = imports.elementAt(i); 076 if (!(idecl instanceof SingleTypeImportDecl)) 077 continue; 078 079 // Get the full name and unqualified name of the type it's importing: 080 Name N1 = ((SingleTypeImportDecl)idecl).typeName.name; 081 Identifier T1 = N1.identifierAt(N1.size()-1); 082 083 // Check for import N1, import Q.T1 pairs: 084 085 for (int j = i+1; j < imports.size(); j++) { 086 // Skip to the next single-type import: 087 ImportDecl jdecl = imports.elementAt(j); 088 if (!(jdecl instanceof SingleTypeImportDecl)) 089 continue; 090 091 // Get the full name and unqualified name of the type it's importing: 092 Name N2 = ((SingleTypeImportDecl)jdecl).typeName.name; 093 Identifier T2 = N2.identifierAt(N2.size()-1); 094 095 if (T1==T2 && !(N1.equals(N2))) 096 ErrorSet.fatal(N1.locIdAt(0), 097 "Attempt to import both " + N1.printName() 098 + " and " + N2.printName() 099 + " as " + T1 100 + " using single-type-import declarations"); 101 } 102 103 // Check to see if N1 is of the form P.T where T is declared in this 104 // CompilationUnit and P is not this CompilationUnit's package: 105 for (int j=0; j<elems.size(); j++) { 106 if (T1 != elems.elementAt(j).id) 107 continue; 108 109 // Skip if N1=<our package>.T1 : 110 if (cu.pkgName==null) { 111 if (N1.size()==1) 112 continue; 113 } else { 114 if (N1.size()>1 && 115 (N1.prefix(N1.size()-1).equals(cu.pkgName))) 116 continue; // @bug fix for inner classes !!!! 117 } 118 119 ErrorSet.fatal(N1.locIdAt(0), 120 N1.printName() 121 + " can not be imported here because " 122 + T1 + " is already defined at " 123 + Location.toString(elems.elementAt(j).loc)); 124 } 125 } 126 } 127 128 // Private methods 129 130 /** 131 * Check a <code>CompilationUnit<code> to make sure that the only type that may 132 * be declared public in it is the one with the same name as the file it occurs 133 * in. Violations result in warnings only. 134 */ 135 136 //@ requires cu != null; 137 private static void checkPublic(CompilationUnit cu) { 138 // Get the basename of the file we loaded cu from: 139 String localname = Location.toFile(cu.loc).getLocalName(); 140 String basename = javafe.filespace.Extension.getBasename(localname); 141 142 // Iterate over all the TypeDecls representing package-member 143 // types in cu: 144 TypeDeclVec elems = cu.elems; 145 for (int i=0; i<elems.size(); i++) { 146 TypeDecl decl = elems.elementAt(i); 147 if (Modifiers.isPublic(decl.modifiers)) { 148 String T = decl.id.toString(); // decl's typename 149 if (!T.equals(basename)) { 150 String msg = 151 ("type " + TypeSig.getSig(decl).getExternalName() 152 + " must be declared in a file named " 153 + T + ".java if it is to be declared public."); 154 ErrorSet.caution(decl.locId, msg); 155 } 156 } 157 } 158 } 159 160 161 /** 162 * Check a <code>CompilationUnit<code> to make sure that each import is 163 * individually well formed. 164 * 165 * <p> In particular, 166 * <ul> 167 * <li> Type in single import must exist </li> 168 * <li> Single imports from other packages must be public (not implemented) 169 * </li> 170 * <li> Packages in all imports must be "accessible" (disabled) </li> 171 * </ul> 172 * <p> This routine does not check legality of pairs of import statements or 173 * import statements and the types declared in the rest of the 174 * CompilationUnit. </p> 175 */ 176 //@ requires cu != null; 177 private static void checkImports(CompilationUnit cu) { 178 ImportDeclVec imports = cu.imports; 179 180 for (int i = 0; i < imports.size(); i++) { 181 ImportDecl idecl = imports.elementAt(i); 182 if (idecl instanceof SingleTypeImportDecl) { 183 // Single-type import: 184 Name N = ((SingleTypeImportDecl)idecl).typeName.name; 185 int sz = N.size(); 186 String[] P = N.toStrings(sz-1); 187 Identifier T = N.identifierAt(sz-1); 188 189 TypeSig r = EnvForCU.lookupWithoutInheritence(null, P, T.toString()); 190 if (r==null) 191 ErrorSet.error(N.getStartLoc(), 192 "No such type: " + 193 PrettyPrint.inst.toString(N)); 194 } else { 195 // On-demand import: 196 /* [disabled] 197 Name N = ((OnDemandImportDecl)idecl).pkgName; 198 int sz = N.size(); 199 if (sz>0) { 200 String[] P = N.toStrings(sz-1); 201 Identifier T = N.identifierAt(sz-1); 202 203 TypeSig r = 204 EnvForCU.lookupWithoutInheritence(P, T.toString()); 205 if (r != null) 206 continue; 207 } 208 if (!OutsideEnv.reader.accessable(N.toStrings())) 209 ErrorSet.error(N.getStartLoc(), 210 PrettyPrint.inst.toString(N) 211 + ": no such type or package"); 212 */ 213 } 214 } 215 } 216 } // end of class CheckCompilationUnit 217 218 /* 219 * Local Variables: 220 * Mode: Java 221 * fill-column: 85 222 * End: 223 */