< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/doclint/Checker.java

Print this page




  44 import javax.lang.model.element.ElementKind;
  45 import javax.lang.model.element.ExecutableElement;
  46 import javax.lang.model.element.Name;
  47 import javax.lang.model.element.VariableElement;
  48 import javax.lang.model.type.TypeKind;
  49 import javax.lang.model.type.TypeMirror;
  50 import javax.tools.Diagnostic.Kind;
  51 import javax.tools.JavaFileObject;
  52 
  53 import com.sun.source.doctree.AttributeTree;
  54 import com.sun.source.doctree.AuthorTree;
  55 import com.sun.source.doctree.DocCommentTree;
  56 import com.sun.source.doctree.DocRootTree;
  57 import com.sun.source.doctree.DocTree;
  58 import com.sun.source.doctree.EndElementTree;
  59 import com.sun.source.doctree.EntityTree;
  60 import com.sun.source.doctree.ErroneousTree;
  61 import com.sun.source.doctree.IdentifierTree;
  62 import com.sun.source.doctree.IndexTree;
  63 import com.sun.source.doctree.InheritDocTree;
  64 import com.sun.source.doctree.InlineTagTree;
  65 import com.sun.source.doctree.LinkTree;
  66 import com.sun.source.doctree.LiteralTree;
  67 import com.sun.source.doctree.ParamTree;
  68 import com.sun.source.doctree.ProvidesTree;
  69 import com.sun.source.doctree.ReferenceTree;
  70 import com.sun.source.doctree.ReturnTree;
  71 import com.sun.source.doctree.SerialDataTree;
  72 import com.sun.source.doctree.SerialFieldTree;
  73 import com.sun.source.doctree.SinceTree;
  74 import com.sun.source.doctree.StartElementTree;
  75 import com.sun.source.doctree.SummaryTree;
  76 import com.sun.source.doctree.SystemPropertyTree;
  77 import com.sun.source.doctree.TextTree;
  78 import com.sun.source.doctree.ThrowsTree;
  79 import com.sun.source.doctree.UnknownBlockTagTree;
  80 import com.sun.source.doctree.UnknownInlineTagTree;
  81 import com.sun.source.doctree.UsesTree;
  82 import com.sun.source.doctree.ValueTree;
  83 import com.sun.source.doctree.VersionTree;
  84 import com.sun.source.tree.Tree;


 123     }
 124 
 125     static class TagStackItem {
 126         final DocTree tree; // typically, but not always, StartElementTree
 127         final HtmlTag tag;
 128         final Set<HtmlTag.Attr> attrs;
 129         final Set<Flag> flags;
 130         TagStackItem(DocTree tree, HtmlTag tag) {
 131             this.tree = tree;
 132             this.tag = tag;
 133             attrs = EnumSet.noneOf(HtmlTag.Attr.class);
 134             flags = EnumSet.noneOf(Flag.class);
 135         }
 136         @Override
 137         public String toString() {
 138             return String.valueOf(tag);
 139         }
 140     }
 141 
 142     private final Deque<TagStackItem> tagStack; // TODO: maybe want to record starting tree as well
 143     private HtmlTag currHeaderTag;
 144 
 145     private final int implicitHeaderLevel;
 146 
 147     // <editor-fold defaultstate="collapsed" desc="Top level">
 148 
 149     Checker(Env env) {
 150         this.env = Assert.checkNonNull(env);
 151         tagStack = new LinkedList<>();
 152         implicitHeaderLevel = env.implicitHeaderLevel;
 153     }
 154 
 155     public Void scan(DocCommentTree tree, TreePath p) {
 156         env.initTypes();
 157         env.setCurrent(p, tree);
 158 
 159         boolean isOverridingMethod = !env.currOverriddenMethods.isEmpty();
 160         JavaFileObject fo = p.getCompilationUnit().getSourceFile();
 161 
 162         if (p.getLeaf().getKind() == Tree.Kind.PACKAGE) {
 163             // If p points to a package, the implied declaration is the
 164             // package declaration (if any) for the compilation unit.
 165             // Handle this case specially, because doc comments are only
 166             // expected in package-info files.
 167             boolean isPkgInfo = fo.isNameCompatible("package-info", JavaFileObject.Kind.SOURCE);
 168             if (tree == null) {
 169                 if (isPkgInfo)
 170                     reportMissing("dc.missing.comment");
 171                 return null;
 172             } else {
 173                 if (!isPkgInfo)
 174                     reportReference("dc.unexpected.comment");
 175             }
 176         } else if (tree != null && fo.isNameCompatible("package", JavaFileObject.Kind.HTML)) {
 177             // a package.html file with a DocCommentTree
 178             if (tree.getFullBody().isEmpty()) {
 179                 reportMissing("dc.missing.comment");
 180                 return null;
 181             }
 182         } else {
 183             if (tree == null) {
 184                 if (!isSynthetic() && !isOverridingMethod)
 185                     reportMissing("dc.missing.comment");
 186                 return null;
 187             }
 188         }
 189 
 190         tagStack.clear();
 191         currHeaderTag = null;
 192 
 193         foundParams.clear();
 194         foundThrows.clear();
 195         foundInheritDoc = false;
 196         foundReturn = false;
 197         hasNonWhitespaceText = false;
 198 































 199         scan(new DocTreePath(p, tree), null);
 200 
 201         if (!isOverridingMethod) {
 202             switch (env.currElement.getKind()) {
 203                 case METHOD:
 204                 case CONSTRUCTOR: {
 205                     ExecutableElement ee = (ExecutableElement) env.currElement;
 206                     checkParamsDocumented(ee.getTypeParameters());
 207                     checkParamsDocumented(ee.getParameters());
 208                     switch (ee.getReturnType().getKind()) {
 209                         case VOID:
 210                         case NONE:
 211                             break;
 212                         default:
 213                             if (!foundReturn
 214                                     && !foundInheritDoc
 215                                     && !env.types.isSameType(ee.getReturnType(), env.java_lang_Void)) {
 216                                 reportMissing("dc.missing.return");
 217                             }
 218                     }


 311                     }
 312                     done = true;
 313                     break;
 314                 } else if (tsi.tag.endKind != HtmlTag.EndKind.OPTIONAL) {
 315                     done = true;
 316                     break;
 317                 }
 318             }
 319             if (!done && HtmlTag.BODY.accepts(t)) {
 320                 while (!tagStack.isEmpty()) {
 321                     warnIfEmpty(tagStack.peek(), null);
 322                     tagStack.pop();
 323                 }
 324             }
 325 
 326             markEnclosingTag(Flag.HAS_ELEMENT);
 327             checkStructure(tree, t);
 328 
 329             // tag specific checks
 330             switch (t) {
 331                 // check for out of sequence headers, such as <h1>...</h1>  <h3>...</h3>
 332                 case H1: case H2: case H3: case H4: case H5: case H6:
 333                     checkHeader(tree, t);
 334                     break;
 335             }
 336 
 337             if (t.flags.contains(HtmlTag.Flag.NO_NEST)) {
 338                 for (TagStackItem i: tagStack) {
 339                     if (t == i.tag) {
 340                         env.messages.warning(HTML, tree, "dc.tag.nested.not.allowed", treeName);
 341                         break;
 342                     }
 343                 }
 344             }
 345         }
 346 
 347         // check for self closing tags, such as <a id="name"/>
 348         if (tree.isSelfClosing()) {
 349             env.messages.error(HTML, tree, "dc.tag.self.closing", treeName);
 350         }
 351 
 352         try {
 353             TagStackItem parent = tagStack.peek();


 429                 }
 430                 break;
 431 
 432             case OTHER:
 433                 switch (t) {
 434                     case SCRIPT:
 435                         // <script> may or may not be allowed, depending on --allow-script-in-comments
 436                         // but we allow it here, and rely on a separate scanner to detect all uses
 437                         // of JavaScript, including <script> tags, and use in attributes, etc.
 438                         break;
 439 
 440                     default:
 441                         env.messages.error(HTML, tree, "dc.tag.not.allowed", treeName);
 442                 }
 443                 return;
 444         }
 445 
 446         env.messages.error(HTML, tree, "dc.tag.not.allowed.here", treeName);
 447     }
 448 
 449     private void checkHeader(StartElementTree tree, HtmlTag tag) {
 450         // verify the new tag
 451         if (getHeaderLevel(tag) > getHeaderLevel(currHeaderTag) + 1) {
 452             if (currHeaderTag == null) {
 453                 env.messages.error(ACCESSIBILITY, tree, "dc.tag.header.sequence.1", tag);

 454             } else {
 455                 env.messages.error(ACCESSIBILITY, tree, "dc.tag.header.sequence.2",
 456                     tag, currHeaderTag);
 457             }



 458         }
 459 
 460         currHeaderTag = tag;
 461     }
 462 
 463     private int getHeaderLevel(HtmlTag tag) {
 464         if (tag == null)
 465             return implicitHeaderLevel;
 466         switch (tag) {
 467             case H1: return 1;
 468             case H2: return 2;
 469             case H3: return 3;
 470             case H4: return 4;
 471             case H5: return 5;
 472             case H6: return 6;
 473             default: throw new IllegalArgumentException();
 474         }
 475     }
 476 
 477     @Override @DefinedBy(Api.COMPILER_TREE)
 478     public Void visitEndElement(EndElementTree tree, Void ignore) {
 479         final Name treeName = tree.getName();
 480         final HtmlTag t = HtmlTag.get(treeName);
 481         if (t == null) {
 482             env.messages.error(HTML, tree, "dc.tag.unknown", treeName);
 483         } else if (t.endKind == HtmlTag.EndKind.NONE) {
 484             env.messages.error(HTML, tree, "dc.tag.end.not.permitted", treeName);
 485         } else {


 649                 }
 650             }
 651         }
 652 
 653         // TODO: basic check on value
 654 
 655         return super.visitAttribute(tree, ignore);
 656     }
 657 
 658     private void validateHtml4Attrs(AttributeTree tree, Name name, AttrKind k) {
 659         switch (k) {
 660             case ALL:
 661             case HTML4:
 662                 break;
 663 
 664             case INVALID:
 665                 env.messages.error(HTML, tree, "dc.attr.unknown", name);
 666                 break;
 667 
 668             case OBSOLETE:
 669                 env.messages.warning(ACCESSIBILITY, tree, "dc.attr.obsolete", name);
 670                 break;
 671 
 672             case USE_CSS:
 673                 env.messages.warning(ACCESSIBILITY, tree, "dc.attr.obsolete.use.css", name);
 674                 break;
 675 
 676             case HTML5:
 677                 env.messages.error(HTML, tree, "dc.attr.not.supported.html4", name);
 678                 break;
 679         }
 680     }
 681 
 682     private void validateHtml5Attrs(AttributeTree tree, Name name, AttrKind k) {
 683         switch (k) {
 684             case ALL:
 685             case HTML5:
 686                 break;
 687 
 688             case INVALID:
 689             case OBSOLETE:
 690             case USE_CSS:
 691             case HTML4:
 692                 env.messages.error(HTML, tree, "dc.attr.not.supported.html5", name);
 693                 break;




  44 import javax.lang.model.element.ElementKind;
  45 import javax.lang.model.element.ExecutableElement;
  46 import javax.lang.model.element.Name;
  47 import javax.lang.model.element.VariableElement;
  48 import javax.lang.model.type.TypeKind;
  49 import javax.lang.model.type.TypeMirror;
  50 import javax.tools.Diagnostic.Kind;
  51 import javax.tools.JavaFileObject;
  52 
  53 import com.sun.source.doctree.AttributeTree;
  54 import com.sun.source.doctree.AuthorTree;
  55 import com.sun.source.doctree.DocCommentTree;
  56 import com.sun.source.doctree.DocRootTree;
  57 import com.sun.source.doctree.DocTree;
  58 import com.sun.source.doctree.EndElementTree;
  59 import com.sun.source.doctree.EntityTree;
  60 import com.sun.source.doctree.ErroneousTree;
  61 import com.sun.source.doctree.IdentifierTree;
  62 import com.sun.source.doctree.IndexTree;
  63 import com.sun.source.doctree.InheritDocTree;

  64 import com.sun.source.doctree.LinkTree;
  65 import com.sun.source.doctree.LiteralTree;
  66 import com.sun.source.doctree.ParamTree;
  67 import com.sun.source.doctree.ProvidesTree;
  68 import com.sun.source.doctree.ReferenceTree;
  69 import com.sun.source.doctree.ReturnTree;
  70 import com.sun.source.doctree.SerialDataTree;
  71 import com.sun.source.doctree.SerialFieldTree;
  72 import com.sun.source.doctree.SinceTree;
  73 import com.sun.source.doctree.StartElementTree;
  74 import com.sun.source.doctree.SummaryTree;
  75 import com.sun.source.doctree.SystemPropertyTree;
  76 import com.sun.source.doctree.TextTree;
  77 import com.sun.source.doctree.ThrowsTree;
  78 import com.sun.source.doctree.UnknownBlockTagTree;
  79 import com.sun.source.doctree.UnknownInlineTagTree;
  80 import com.sun.source.doctree.UsesTree;
  81 import com.sun.source.doctree.ValueTree;
  82 import com.sun.source.doctree.VersionTree;
  83 import com.sun.source.tree.Tree;


 122     }
 123 
 124     static class TagStackItem {
 125         final DocTree tree; // typically, but not always, StartElementTree
 126         final HtmlTag tag;
 127         final Set<HtmlTag.Attr> attrs;
 128         final Set<Flag> flags;
 129         TagStackItem(DocTree tree, HtmlTag tag) {
 130             this.tree = tree;
 131             this.tag = tag;
 132             attrs = EnumSet.noneOf(HtmlTag.Attr.class);
 133             flags = EnumSet.noneOf(Flag.class);
 134         }
 135         @Override
 136         public String toString() {
 137             return String.valueOf(tag);
 138         }
 139     }
 140 
 141     private final Deque<TagStackItem> tagStack; // TODO: maybe want to record starting tree as well
 142     private HtmlTag currHeadingTag;
 143 
 144     private int implicitHeadingRank;
 145 
 146     // <editor-fold defaultstate="collapsed" desc="Top level">
 147 
 148     Checker(Env env) {
 149         this.env = Assert.checkNonNull(env);
 150         tagStack = new LinkedList<>();

 151     }
 152 
 153     public Void scan(DocCommentTree tree, TreePath p) {
 154         env.initTypes();
 155         env.setCurrent(p, tree);
 156 
 157         boolean isOverridingMethod = !env.currOverriddenMethods.isEmpty();
 158         JavaFileObject fo = p.getCompilationUnit().getSourceFile();
 159 
 160         if (p.getLeaf().getKind() == Tree.Kind.PACKAGE) {
 161             // If p points to a package, the implied declaration is the
 162             // package declaration (if any) for the compilation unit.
 163             // Handle this case specially, because doc comments are only
 164             // expected in package-info files.
 165             boolean isPkgInfo = fo.isNameCompatible("package-info", JavaFileObject.Kind.SOURCE);
 166             if (tree == null) {
 167                 if (isPkgInfo)
 168                     reportMissing("dc.missing.comment");
 169                 return null;
 170             } else {
 171                 if (!isPkgInfo)
 172                     reportReference("dc.unexpected.comment");
 173             }
 174         } else if (tree != null && fo.isNameCompatible("package", JavaFileObject.Kind.HTML)) {
 175             // a package.html file with a DocCommentTree
 176             if (tree.getFullBody().isEmpty()) {
 177                 reportMissing("dc.missing.comment");
 178                 return null;
 179             }
 180         } else {
 181             if (tree == null) {
 182                 if (!isSynthetic() && !isOverridingMethod)
 183                     reportMissing("dc.missing.comment");
 184                 return null;
 185             }
 186         }
 187 
 188         tagStack.clear();
 189         currHeadingTag = null;
 190 
 191         foundParams.clear();
 192         foundThrows.clear();
 193         foundInheritDoc = false;
 194         foundReturn = false;
 195         hasNonWhitespaceText = false;
 196 
 197         switch (p.getLeaf().getKind()) {
 198             // the following are for declarations that have their own top-level page,
 199             // and so the doc comment comes after the <h1> page title.
 200             case MODULE:
 201             case PACKAGE:
 202             case CLASS:
 203             case INTERFACE:
 204             case ENUM:
 205             case ANNOTATION_TYPE:
 206                 implicitHeadingRank = 1;
 207                 break;
 208 
 209             // this is for html files
 210             // ... if it is a legacy package.html, the doc comment comes after the <h1> page title
 211             // ... otherwise, (e.g. overview file and doc-files/*.html files) no additional headings are inserted
 212             case COMPILATION_UNIT:
 213                 implicitHeadingRank = fo.isNameCompatible("package", JavaFileObject.Kind.HTML) ? 1 : 0;
 214                 break;
 215 
 216             // the following are for member declarations, which appear in the page
 217             // for the enclosing type, and so appear after the <h2> "Members"
 218             // aggregate heading and the specific <h3> "Member signature" heading.
 219             case METHOD:
 220             case VARIABLE:
 221                 implicitHeadingRank = 3;
 222                 break;
 223 
 224             default:
 225                 Assert.error("unexpected tree kind: " + p.getLeaf().getKind() + " " + fo);
 226         }
 227 
 228         scan(new DocTreePath(p, tree), null);
 229 
 230         if (!isOverridingMethod) {
 231             switch (env.currElement.getKind()) {
 232                 case METHOD:
 233                 case CONSTRUCTOR: {
 234                     ExecutableElement ee = (ExecutableElement) env.currElement;
 235                     checkParamsDocumented(ee.getTypeParameters());
 236                     checkParamsDocumented(ee.getParameters());
 237                     switch (ee.getReturnType().getKind()) {
 238                         case VOID:
 239                         case NONE:
 240                             break;
 241                         default:
 242                             if (!foundReturn
 243                                     && !foundInheritDoc
 244                                     && !env.types.isSameType(ee.getReturnType(), env.java_lang_Void)) {
 245                                 reportMissing("dc.missing.return");
 246                             }
 247                     }


 340                     }
 341                     done = true;
 342                     break;
 343                 } else if (tsi.tag.endKind != HtmlTag.EndKind.OPTIONAL) {
 344                     done = true;
 345                     break;
 346                 }
 347             }
 348             if (!done && HtmlTag.BODY.accepts(t)) {
 349                 while (!tagStack.isEmpty()) {
 350                     warnIfEmpty(tagStack.peek(), null);
 351                     tagStack.pop();
 352                 }
 353             }
 354 
 355             markEnclosingTag(Flag.HAS_ELEMENT);
 356             checkStructure(tree, t);
 357 
 358             // tag specific checks
 359             switch (t) {
 360                 // check for out of sequence headings, such as <h1>...</h1>  <h3>...</h3>
 361                 case H1: case H2: case H3: case H4: case H5: case H6:
 362                     checkHeading(tree, t);
 363                     break;
 364             }
 365 
 366             if (t.flags.contains(HtmlTag.Flag.NO_NEST)) {
 367                 for (TagStackItem i: tagStack) {
 368                     if (t == i.tag) {
 369                         env.messages.warning(HTML, tree, "dc.tag.nested.not.allowed", treeName);
 370                         break;
 371                     }
 372                 }
 373             }
 374         }
 375 
 376         // check for self closing tags, such as <a id="name"/>
 377         if (tree.isSelfClosing()) {
 378             env.messages.error(HTML, tree, "dc.tag.self.closing", treeName);
 379         }
 380 
 381         try {
 382             TagStackItem parent = tagStack.peek();


 458                 }
 459                 break;
 460 
 461             case OTHER:
 462                 switch (t) {
 463                     case SCRIPT:
 464                         // <script> may or may not be allowed, depending on --allow-script-in-comments
 465                         // but we allow it here, and rely on a separate scanner to detect all uses
 466                         // of JavaScript, including <script> tags, and use in attributes, etc.
 467                         break;
 468 
 469                     default:
 470                         env.messages.error(HTML, tree, "dc.tag.not.allowed", treeName);
 471                 }
 472                 return;
 473         }
 474 
 475         env.messages.error(HTML, tree, "dc.tag.not.allowed.here", treeName);
 476     }
 477 
 478     private void checkHeading(StartElementTree tree, HtmlTag tag) {
 479         // verify the new tag
 480         if (getHeadingRank(tag) > getHeadingRank(currHeadingTag) + 1) {
 481             if (currHeadingTag == null) {
 482                 env.messages.error(ACCESSIBILITY, tree, "dc.tag.heading.sequence.1",
 483                         tag, implicitHeadingRank);
 484             } else {
 485                 env.messages.error(ACCESSIBILITY, tree, "dc.tag.heading.sequence.2",
 486                     tag, currHeadingTag);
 487             }
 488         } else if (getHeadingRank(tag) <= implicitHeadingRank) {
 489             env.messages.error(ACCESSIBILITY, tree, "dc.tag.heading.sequence.3",
 490                     tag, implicitHeadingRank);
 491         }
 492 
 493         currHeadingTag = tag;
 494     }
 495 
 496     private int getHeadingRank(HtmlTag tag) {
 497         if (tag == null)
 498             return implicitHeadingRank;
 499         switch (tag) {
 500             case H1: return 1;
 501             case H2: return 2;
 502             case H3: return 3;
 503             case H4: return 4;
 504             case H5: return 5;
 505             case H6: return 6;
 506             default: throw new IllegalArgumentException();
 507         }
 508     }
 509 
 510     @Override @DefinedBy(Api.COMPILER_TREE)
 511     public Void visitEndElement(EndElementTree tree, Void ignore) {
 512         final Name treeName = tree.getName();
 513         final HtmlTag t = HtmlTag.get(treeName);
 514         if (t == null) {
 515             env.messages.error(HTML, tree, "dc.tag.unknown", treeName);
 516         } else if (t.endKind == HtmlTag.EndKind.NONE) {
 517             env.messages.error(HTML, tree, "dc.tag.end.not.permitted", treeName);
 518         } else {


 682                 }
 683             }
 684         }
 685 
 686         // TODO: basic check on value
 687 
 688         return super.visitAttribute(tree, ignore);
 689     }
 690 
 691     private void validateHtml4Attrs(AttributeTree tree, Name name, AttrKind k) {
 692         switch (k) {
 693             case ALL:
 694             case HTML4:
 695                 break;
 696 
 697             case INVALID:
 698                 env.messages.error(HTML, tree, "dc.attr.unknown", name);
 699                 break;
 700 
 701             case OBSOLETE:
 702                 env.messages.warning(HTML, tree, "dc.attr.obsolete", name);
 703                 break;
 704 
 705             case USE_CSS:
 706                 env.messages.warning(HTML, tree, "dc.attr.obsolete.use.css", name);
 707                 break;
 708 
 709             case HTML5:
 710                 env.messages.error(HTML, tree, "dc.attr.not.supported.html4", name);
 711                 break;
 712         }
 713     }
 714 
 715     private void validateHtml5Attrs(AttributeTree tree, Name name, AttrKind k) {
 716         switch (k) {
 717             case ALL:
 718             case HTML5:
 719                 break;
 720 
 721             case INVALID:
 722             case OBSOLETE:
 723             case USE_CSS:
 724             case HTML4:
 725                 env.messages.error(HTML, tree, "dc.attr.not.supported.html5", name);
 726                 break;


< prev index next >