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