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 */