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