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

Sun, 16 Dec 2012 11:09:36 +0100

author
jfranck
date
Sun, 16 Dec 2012 11:09:36 +0100
changeset 1464
f72c9c5aeaef
parent 1455
75ab654b5cd5
child 1495
bc1023e0e533
permissions
-rw-r--r--

8005098: Provide isSynthesized() information on Attribute.Compound
Reviewed-by: jjg

     1 /*
     2  * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package com.sun.tools.doclint;
    28 import java.util.regex.Matcher;
    29 import com.sun.source.doctree.LinkTree;
    30 import java.net.URI;
    31 import java.util.regex.Pattern;
    32 import java.io.IOException;
    33 import com.sun.tools.javac.tree.DocPretty;
    34 import java.io.StringWriter;
    35 import java.util.Deque;
    36 import java.util.EnumSet;
    37 import java.util.HashSet;
    38 import java.util.LinkedList;
    39 import java.util.List;
    40 import java.util.Set;
    42 import javax.lang.model.element.Element;
    43 import javax.lang.model.element.ElementKind;
    44 import javax.lang.model.element.ExecutableElement;
    45 import javax.lang.model.element.Name;
    46 import javax.lang.model.element.TypeElement;
    47 import javax.lang.model.type.TypeKind;
    48 import javax.lang.model.type.TypeMirror;
    49 import javax.tools.Diagnostic.Kind;
    51 import com.sun.source.doctree.AttributeTree;
    52 import com.sun.source.doctree.AuthorTree;
    53 import com.sun.source.doctree.DocCommentTree;
    54 import com.sun.source.doctree.DocTree;
    55 import com.sun.source.doctree.EndElementTree;
    56 import com.sun.source.doctree.EntityTree;
    57 import com.sun.source.doctree.ErroneousTree;
    58 import com.sun.source.doctree.IdentifierTree;
    59 import com.sun.source.doctree.InheritDocTree;
    60 import com.sun.source.doctree.ParamTree;
    61 import com.sun.source.doctree.ReferenceTree;
    62 import com.sun.source.doctree.ReturnTree;
    63 import com.sun.source.doctree.SerialDataTree;
    64 import com.sun.source.doctree.SerialFieldTree;
    65 import com.sun.source.doctree.SinceTree;
    66 import com.sun.source.doctree.StartElementTree;
    67 import com.sun.source.doctree.TextTree;
    68 import com.sun.source.doctree.ThrowsTree;
    69 import com.sun.source.doctree.VersionTree;
    70 import com.sun.source.util.DocTreeScanner;
    71 import com.sun.source.util.TreePath;
    72 import com.sun.tools.doclint.HtmlTag.AttrKind;
    73 import java.net.URISyntaxException;
    74 import static com.sun.tools.doclint.Messages.Group.*;
    77 /**
    78  * Validate a doc comment.
    79  *
    80  * <p><b>This is NOT part of any supported API.
    81  * If you write code that depends on this, you do so at your own
    82  * risk.  This code and its internal interfaces are subject to change
    83  * or deletion without notice.</b></p>
    84  */
    85 public class Checker extends DocTreeScanner<Void, Void> {
    86     final Env env;
    88     Set<Element> foundParams = new HashSet<Element>();
    89     Set<TypeMirror> foundThrows = new HashSet<TypeMirror>();
    90     Set<String> foundAnchors = new HashSet<String>();
    91     boolean foundInheritDoc = false;
    92     boolean foundReturn = false;
    94     enum Flag {
    95         TABLE_HAS_CAPTION,
    96         HAS_ELEMENT,
    97         HAS_TEXT
    98     }
   100     static class TagStackItem {
   101         final DocTree tree; // typically, but not always, StartElementTree
   102         final HtmlTag tag;
   103         final Set<HtmlTag.Attr> attrs;
   104         final Set<Flag> flags;
   105         TagStackItem(DocTree tree, HtmlTag tag) {
   106             this.tree = tree;
   107             this.tag = tag;
   108             attrs = EnumSet.noneOf(HtmlTag.Attr.class);
   109             flags = EnumSet.noneOf(Flag.class);
   110         }
   111         @Override
   112         public String toString() {
   113             return String.valueOf(tag);
   114         }
   115     }
   117     private Deque<TagStackItem> tagStack; // TODO: maybe want to record starting tree as well
   118     private HtmlTag currHeaderTag;
   120     // <editor-fold defaultstate="collapsed" desc="Top level">
   122     Checker(Env env) {
   123         env.getClass();
   124         this.env = env;
   125         tagStack = new LinkedList<TagStackItem>();
   126     }
   128     public Void scan(DocCommentTree tree, TreePath p) {
   129         env.setCurrent(p, tree);
   131         boolean isOverridingMethod = !env.currOverriddenMethods.isEmpty();
   133         if (tree == null) {
   134             if (!isSynthetic() && !isOverridingMethod)
   135                 reportMissing("dc.missing.comment");
   136             return null;
   137         }
   139         tagStack.clear();
   140         currHeaderTag = null;
   142         foundParams.clear();
   143         foundThrows.clear();
   144         foundInheritDoc = false;
   145         foundReturn = false;
   147         scan(tree, (Void) null);
   149         if (!isOverridingMethod) {
   150             switch (env.currElement.getKind()) {
   151                 case METHOD:
   152                 case CONSTRUCTOR: {
   153                     ExecutableElement ee = (ExecutableElement) env.currElement;
   154                     checkParamsDocumented(ee.getTypeParameters());
   155                     checkParamsDocumented(ee.getParameters());
   156                     switch (ee.getReturnType().getKind()) {
   157                         case VOID:
   158                         case NONE:
   159                             break;
   160                         default:
   161                             if (!foundReturn
   162                                     && !foundInheritDoc
   163                                     && !env.types.isSameType(ee.getReturnType(), env.java_lang_Void)) {
   164                                 reportMissing("dc.missing.return");
   165                             }
   166                     }
   167                     checkThrowsDocumented(ee.getThrownTypes());
   168                 }
   169             }
   170         }
   172         return null;
   173     }
   175     private void reportMissing(String code, Object... args) {
   176         env.messages.report(MISSING, Kind.WARNING, env.currPath.getLeaf(), code, args);
   177     }
   179     @Override
   180     public Void visitDocComment(DocCommentTree tree, Void ignore) {
   181         super.visitDocComment(tree, ignore);
   182         for (TagStackItem tsi: tagStack) {
   183             if (tsi.tree.getKind() == DocTree.Kind.START_ELEMENT
   184                     && tsi.tag.endKind == HtmlTag.EndKind.REQUIRED) {
   185                 StartElementTree t = (StartElementTree) tsi.tree;
   186                 env.messages.error(HTML, t, "dc.tag.not.closed", t.getName());
   187             }
   188         }
   189         return null;
   190     }
   191     // </editor-fold>
   193     // <editor-fold defaultstate="collapsed" desc="Text and entities.">
   195     @Override
   196     public Void visitText(TextTree tree, Void ignore) {
   197         if (!tree.getBody().trim().isEmpty()) {
   198             markEnclosingTag(Flag.HAS_TEXT);
   199         }
   200         return null;
   201     }
   203     @Override
   204     public Void visitEntity(EntityTree tree, Void ignore) {
   205         markEnclosingTag(Flag.HAS_TEXT);
   206         String name = tree.getName().toString();
   207         if (name.startsWith("#")) {
   208             int v = name.toLowerCase().startsWith("#x")
   209                     ? Integer.parseInt(name.substring(2), 16)
   210                     : Integer.parseInt(name.substring(1), 10);
   211             if (!Entity.isValid(v)) {
   212                 env.messages.error(HTML, tree, "dc.entity.invalid", name);
   213             }
   214         } else if (!Entity.isValid(name)) {
   215             env.messages.error(HTML, tree, "dc.entity.invalid", name);
   216         }
   217         return null;
   218     }
   220     // </editor-fold>
   222     // <editor-fold defaultstate="collapsed" desc="HTML elements">
   224     @Override
   225     public Void visitStartElement(StartElementTree tree, Void ignore) {
   226         markEnclosingTag(Flag.HAS_ELEMENT);
   227         final Name treeName = tree.getName();
   228         final HtmlTag t = HtmlTag.get(treeName);
   229         if (t == null) {
   230             env.messages.error(HTML, tree, "dc.tag.unknown", treeName);
   231         } else {
   232             // tag specific checks
   233             switch (t) {
   234                 // check for out of sequence headers, such as <h1>...</h1>  <h3>...</h3>
   235                 case H1: case H2: case H3: case H4: case H5: case H6:
   236                     checkHeader(tree, t);
   237                     break;
   238                 // <p> inside <pre>
   239                 case P:
   240                     TagStackItem top = tagStack.peek();
   241                     if (top != null && top.tag == HtmlTag.PRE)
   242                         env.messages.warning(HTML, tree, "dc.tag.p.in.pre");
   243                     break;
   244             }
   246             // check that only block tags and inline tags are used,
   247             // and that blocks tags are not used within inline tags
   248             switch (t.blockType) {
   249                 case INLINE:
   250                     break;
   251                 case BLOCK:
   252                     TagStackItem top = tagStack.peek();
   253                     if (top != null && top.tag != null && top.tag.blockType == HtmlTag.BlockType.INLINE) {
   254                         switch (top.tree.getKind()) {
   255                             case START_ELEMENT: {
   256                                 Name name = ((StartElementTree) top.tree).getName();
   257                                 env.messages.error(HTML, tree, "dc.tag.not.allowed.inline.element",
   258                                         treeName, name);
   259                                 break;
   260                             }
   261                             case LINK:
   262                             case LINK_PLAIN: {
   263                                 String name = top.tree.getKind().tagName;
   264                                 env.messages.error(HTML, tree, "dc.tag.not.allowed.inline.tag",
   265                                         treeName, name);
   266                                 break;
   267                             }
   268                             default:
   269                                 env.messages.error(HTML, tree, "dc.tag.not.allowed.inline.other",
   270                                         treeName);
   271                         }
   272                     }
   273                     break;
   274                 case OTHER:
   275                     env.messages.error(HTML, tree, "dc.tag.not.allowed", treeName);
   276                     break;
   277                 default:
   278                     throw new AssertionError();
   279             }
   281             if (t.flags.contains(HtmlTag.Flag.NO_NEST)) {
   282                 for (TagStackItem i: tagStack) {
   283                     if (t == i.tag) {
   284                         env.messages.warning(HTML, tree, "dc.tag.nested.not.allowed", treeName);
   285                         break;
   286                     }
   287                 }
   288             }
   289         }
   291         // check for self closing tags, such as <a id="name"/>
   292         if (tree.isSelfClosing()) {
   293             env.messages.error(HTML, tree, "dc.tag.self.closing", treeName);
   294         }
   296         try {
   297             TagStackItem parent = tagStack.peek();
   298             TagStackItem top = new TagStackItem(tree, t);
   299             tagStack.push(top);
   301             super.visitStartElement(tree, ignore);
   303             // handle attributes that may or may not have been found in start element
   304             if (t != null) {
   305                 switch (t) {
   306                     case CAPTION:
   307                         if (parent != null && parent.tag == HtmlTag.TABLE)
   308                             parent.flags.add(Flag.TABLE_HAS_CAPTION);
   309                         break;
   311                     case IMG:
   312                         if (!top.attrs.contains(HtmlTag.Attr.ALT))
   313                             env.messages.error(ACCESSIBILITY, tree, "dc.no.alt.attr.for.image");
   314                         break;
   315                 }
   316             }
   318             return null;
   319         } finally {
   321             if (t == null || t.endKind == HtmlTag.EndKind.NONE)
   322                 tagStack.pop();
   323         }
   324     }
   326     private void checkHeader(StartElementTree tree, HtmlTag tag) {
   327         // verify the new tag
   328         if (getHeaderLevel(tag) > getHeaderLevel(currHeaderTag) + 1) {
   329             if (currHeaderTag == null) {
   330                 env.messages.error(ACCESSIBILITY, tree, "dc.tag.header.sequence.1", tag);
   331             } else {
   332                 env.messages.error(ACCESSIBILITY, tree, "dc.tag.header.sequence.2",
   333                     tag, currHeaderTag);
   334             }
   335         }
   337         currHeaderTag = tag;
   338     }
   340     private int getHeaderLevel(HtmlTag tag) {
   341         if (tag == null)
   342             return 0;
   343         switch (tag) {
   344             case H1: return 1;
   345             case H2: return 2;
   346             case H3: return 3;
   347             case H4: return 4;
   348             case H5: return 5;
   349             case H6: return 6;
   350             default: throw new IllegalArgumentException();
   351         }
   352     }
   354     @Override
   355     public Void visitEndElement(EndElementTree tree, Void ignore) {
   356         final Name treeName = tree.getName();
   357         final HtmlTag t = HtmlTag.get(treeName);
   358         if (t == null) {
   359             env.messages.error(HTML, tree, "dc.tag.unknown", treeName);
   360         } else if (t.endKind == HtmlTag.EndKind.NONE) {
   361             env.messages.error(HTML, tree, "dc.tag.end.not.permitted", treeName);
   362         } else if (tagStack.isEmpty()) {
   363             env.messages.error(HTML, tree, "dc.tag.end.unexpected", treeName);
   364         } else {
   365             while (!tagStack.isEmpty()) {
   366                 TagStackItem top = tagStack.peek();
   367                 if (t == top.tag) {
   368                     switch (t) {
   369                         case TABLE:
   370                             if (!top.attrs.contains(HtmlTag.Attr.SUMMARY)
   371                                     && !top.flags.contains(Flag.TABLE_HAS_CAPTION)) {
   372                                 env.messages.error(ACCESSIBILITY, tree,
   373                                         "dc.no.summary.or.caption.for.table");
   374                             }
   375                     }
   376                     if (t.flags.contains(HtmlTag.Flag.EXPECT_CONTENT)
   377                             && !top.flags.contains(Flag.HAS_TEXT)
   378                             && !top.flags.contains(Flag.HAS_ELEMENT)) {
   379                         env.messages.warning(HTML, tree, "dc.tag.empty", treeName);
   380                     }
   381                     if (t.flags.contains(HtmlTag.Flag.NO_TEXT)
   382                             && top.flags.contains(Flag.HAS_TEXT)) {
   383                         env.messages.error(HTML, tree, "dc.text.not.allowed", treeName);
   384                     }
   385                     tagStack.pop();
   386                     break;
   387                 } else if (top.tag == null || top.tag.endKind != HtmlTag.EndKind.REQUIRED) {
   388                     tagStack.pop();
   389                 } else {
   390                     boolean found = false;
   391                     for (TagStackItem si: tagStack) {
   392                         if (si.tag == t) {
   393                             found = true;
   394                             break;
   395                         }
   396                     }
   397                     if (found && top.tree.getKind() == DocTree.Kind.START_ELEMENT) {
   398                         env.messages.error(HTML, top.tree, "dc.tag.start.unmatched",
   399                                 ((StartElementTree) top.tree).getName());
   400                         tagStack.pop();
   401                     } else {
   402                         env.messages.error(HTML, tree, "dc.tag.end.unexpected", treeName);
   403                         break;
   404                     }
   405                 }
   406             }
   407         }
   409         return super.visitEndElement(tree, ignore);
   410     }
   411     // </editor-fold>
   413     // <editor-fold defaultstate="collapsed" desc="HTML attributes">
   415     @Override @SuppressWarnings("fallthrough")
   416     public Void visitAttribute(AttributeTree tree, Void ignore) {
   417         HtmlTag currTag = tagStack.peek().tag;
   418         if (currTag != null) {
   419             Name name = tree.getName();
   420             HtmlTag.Attr attr = currTag.getAttr(name);
   421             if (attr != null) {
   422                 boolean first = tagStack.peek().attrs.add(attr);
   423                 if (!first)
   424                     env.messages.error(HTML, tree, "dc.attr.repeated", name);
   425             }
   426             AttrKind k = currTag.getAttrKind(name);
   427             switch (k) {
   428                 case OK:
   429                     break;
   431                 case INVALID:
   432                     env.messages.error(HTML, tree, "dc.attr.unknown", name);
   433                     break;
   435                 case OBSOLETE:
   436                     env.messages.warning(ACCESSIBILITY, tree, "dc.attr.obsolete", name);
   437                     break;
   439                 case USE_CSS:
   440                     env.messages.warning(ACCESSIBILITY, tree, "dc.attr.obsolete.use.css", name);
   441                     break;
   442             }
   444             if (attr != null) {
   445                 switch (attr) {
   446                     case NAME:
   447                         if (currTag != HtmlTag.A) {
   448                             break;
   449                         }
   450                     // fallthrough
   451                     case ID:
   452                         String value = getAttrValue(tree);
   453                         if (!validName.matcher(value).matches()) {
   454                             env.messages.error(HTML, tree, "dc.invalid.anchor", value);
   455                         }
   456                         if (!foundAnchors.add(value)) {
   457                             env.messages.error(HTML, tree, "dc.anchor.already.defined", value);
   458                         }
   459                         break;
   461                     case HREF:
   462                         if (currTag == HtmlTag.A) {
   463                             String v = getAttrValue(tree);
   464                             if (v == null || v.isEmpty()) {
   465                                 env.messages.error(HTML, tree, "dc.attr.lacks.value");
   466                             } else {
   467                                 Matcher m = docRoot.matcher(v);
   468                                 if (m.matches()) {
   469                                     String rest = m.group(2);
   470                                     if (!rest.isEmpty())
   471                                         checkURI(tree, rest);
   472                                 } else {
   473                                     checkURI(tree, v);
   474                                 }
   475                             }
   476                         }
   477                         break;
   478                 }
   479             }
   480         }
   482         // TODO: basic check on value
   484         return super.visitAttribute(tree, ignore);
   485     }
   487     // http://www.w3.org/TR/html401/types.html#type-name
   488     private static final Pattern validName = Pattern.compile("[A-Za-z][A-Za-z0-9-_:.]*");
   490     // pattern to remove leading {@docRoot}/?
   491     private static final Pattern docRoot = Pattern.compile("(?i)(\\{@docRoot *\\}/?)?(.*)");
   493     private String getAttrValue(AttributeTree tree) {
   494         if (tree.getValue() == null)
   495             return null;
   497         StringWriter sw = new StringWriter();
   498         try {
   499             new DocPretty(sw).print(tree.getValue());
   500         } catch (IOException e) {
   501             // cannot happen
   502         }
   503         // ignore potential use of entities for now
   504         return sw.toString();
   505     }
   507     private void checkURI(AttributeTree tree, String uri) {
   508         try {
   509             URI u = new URI(uri);
   510         } catch (URISyntaxException e) {
   511             env.messages.error(HTML, tree, "dc.invalid.uri", uri);
   512         }
   513     }
   514     // </editor-fold>
   516     // <editor-fold defaultstate="collapsed" desc="javadoc tags">
   518     @Override
   519     public Void visitAuthor(AuthorTree tree, Void ignore) {
   520         warnIfEmpty(tree, tree.getName());
   521         return super.visitAuthor(tree, ignore);
   522     }
   524     @Override
   525     public Void visitInheritDoc(InheritDocTree tree, Void ignore) {
   526         // TODO: verify on overridden method
   527         foundInheritDoc = true;
   528         return super.visitInheritDoc(tree, ignore);
   529     }
   531     @Override
   532     public Void visitLink(LinkTree tree, Void ignore) {
   533         // simulate inline context on tag stack
   534         HtmlTag t = (tree.getKind() == DocTree.Kind.LINK)
   535                 ? HtmlTag.CODE : HtmlTag.SPAN;
   536         tagStack.push(new TagStackItem(tree, t));
   537         try {
   538             return super.visitLink(tree, ignore);
   539         } finally {
   540             tagStack.pop();
   541         }
   542     }
   544     @Override
   545     public Void visitParam(ParamTree tree, Void ignore) {
   546         boolean typaram = tree.isTypeParameter();
   547         IdentifierTree nameTree = tree.getName();
   548         Element e = env.currElement;
   549         switch (e.getKind()) {
   550             case METHOD: case CONSTRUCTOR: {
   551                 ExecutableElement ee = (ExecutableElement) e;
   552                 checkParamDeclared(nameTree, typaram ? ee.getTypeParameters() : ee.getParameters());
   553                 break;
   554             }
   556             case CLASS: case INTERFACE: {
   557                 TypeElement te = (TypeElement) e;
   558                 if (typaram) {
   559                     checkParamDeclared(nameTree, te.getTypeParameters());
   560                 } else {
   561                     env.messages.error(REFERENCE, tree, "dc.invalid.param");
   562                 }
   563                 break;
   564             }
   566             default:
   567                 env.messages.error(REFERENCE, tree, "dc.invalid.param");
   568                 break;
   569         }
   570         warnIfEmpty(tree, tree.getDescription());
   571         return super.visitParam(tree, ignore);
   572     }
   573     // where
   574     private void checkParamDeclared(IdentifierTree nameTree, List<? extends Element> list) {
   575         Name name = nameTree.getName();
   576         boolean found = false;
   577         for (Element e: list) {
   578             if (name.equals(e.getSimpleName())) {
   579                 foundParams.add(e);
   580                 found = true;
   581             }
   582         }
   583         if (!found)
   584             env.messages.error(REFERENCE, nameTree, "dc.param.name.not.found");
   585     }
   587     private void checkParamsDocumented(List<? extends Element> list) {
   588         if (foundInheritDoc)
   589             return;
   591         for (Element e: list) {
   592             if (!foundParams.contains(e)) {
   593                 CharSequence paramName = (e.getKind() == ElementKind.TYPE_PARAMETER)
   594                         ? "<" + e.getSimpleName() + ">"
   595                         : e.getSimpleName();
   596                 reportMissing("dc.missing.param", paramName);
   597             }
   598         }
   599     }
   601     @Override
   602     public Void visitReference(ReferenceTree tree, Void ignore) {
   603         Element e = env.trees.getElement(env.currPath, tree);
   604         if (e == null)
   605             env.messages.error(REFERENCE, tree, "dc.ref.not.found");
   606         return super.visitReference(tree, ignore);
   607     }
   609     @Override
   610     public Void visitReturn(ReturnTree tree, Void ignore) {
   611         Element e = env.trees.getElement(env.currPath);
   612         if (e.getKind() != ElementKind.METHOD
   613                 || ((ExecutableElement) e).getReturnType().getKind() == TypeKind.VOID)
   614             env.messages.error(REFERENCE, tree, "dc.invalid.return");
   615         foundReturn = true;
   616         warnIfEmpty(tree, tree.getDescription());
   617         return super.visitReturn(tree, ignore);
   618     }
   620     @Override
   621     public Void visitSerialData(SerialDataTree tree, Void ignore) {
   622         warnIfEmpty(tree, tree.getDescription());
   623         return super.visitSerialData(tree, ignore);
   624     }
   626     @Override
   627     public Void visitSerialField(SerialFieldTree tree, Void ignore) {
   628         warnIfEmpty(tree, tree.getDescription());
   629         return super.visitSerialField(tree, ignore);
   630     }
   632     @Override
   633     public Void visitSince(SinceTree tree, Void ignore) {
   634         warnIfEmpty(tree, tree.getBody());
   635         return super.visitSince(tree, ignore);
   636     }
   638     @Override
   639     public Void visitThrows(ThrowsTree tree, Void ignore) {
   640         ReferenceTree exName = tree.getExceptionName();
   641         Element ex = env.trees.getElement(env.currPath, exName);
   642         if (ex == null) {
   643             env.messages.error(REFERENCE, tree, "dc.ref.not.found");
   644         } else if (ex.asType().getKind() == TypeKind.DECLARED
   645                 && env.types.isAssignable(ex.asType(), env.java_lang_Throwable)) {
   646             switch (env.currElement.getKind()) {
   647                 case CONSTRUCTOR:
   648                 case METHOD:
   649                     if (isCheckedException(ex.asType())) {
   650                         ExecutableElement ee = (ExecutableElement) env.currElement;
   651                         checkThrowsDeclared(exName, ex.asType(), ee.getThrownTypes());
   652                     }
   653                     break;
   654                 default:
   655                     env.messages.error(REFERENCE, tree, "dc.invalid.throws");
   656             }
   657         } else {
   658             env.messages.error(REFERENCE, tree, "dc.invalid.throws");
   659         }
   660         warnIfEmpty(tree, tree.getDescription());
   661         return scan(tree.getDescription(), ignore);
   662     }
   664     private void checkThrowsDeclared(ReferenceTree tree, TypeMirror t, List<? extends TypeMirror> list) {
   665         boolean found = false;
   666         for (TypeMirror tl : list) {
   667             if (env.types.isAssignable(t, tl)) {
   668                 foundThrows.add(tl);
   669                 found = true;
   670             }
   671         }
   672         if (!found)
   673             env.messages.error(REFERENCE, tree, "dc.exception.not.thrown", t);
   674     }
   676     private void checkThrowsDocumented(List<? extends TypeMirror> list) {
   677         if (foundInheritDoc)
   678             return;
   680         for (TypeMirror tl: list) {
   681             if (isCheckedException(tl) && !foundThrows.contains(tl))
   682                 reportMissing("dc.missing.throws", tl);
   683         }
   684     }
   686     @Override
   687     public Void visitVersion(VersionTree tree, Void ignore) {
   688         warnIfEmpty(tree, tree.getBody());
   689         return super.visitVersion(tree, ignore);
   690     }
   692     @Override
   693     public Void visitErroneous(ErroneousTree tree, Void ignore) {
   694         env.messages.error(SYNTAX, tree, null, tree.getDiagnostic().getMessage(null));
   695         return null;
   696     }
   697     // </editor-fold>
   699     // <editor-fold defaultstate="collapsed" desc="Utility methods">
   701     private boolean isCheckedException(TypeMirror t) {
   702         return !(env.types.isAssignable(t, env.java_lang_Error)
   703                 || env.types.isAssignable(t, env.java_lang_RuntimeException));
   704     }
   706     private boolean isSynthetic() {
   707         switch (env.currElement.getKind()) {
   708             case CONSTRUCTOR:
   709                 // A synthetic default constructor has the same pos as the
   710                 // enclosing class
   711                 TreePath p = env.currPath;
   712                 return env.getPos(p) == env.getPos(p.getParentPath());
   713         }
   714         return false;
   715     }
   717     void markEnclosingTag(Flag flag) {
   718         TagStackItem top = tagStack.peek();
   719         if (top != null)
   720             top.flags.add(flag);
   721     }
   723     String toString(TreePath p) {
   724         StringBuilder sb = new StringBuilder("TreePath[");
   725         toString(p, sb);
   726         sb.append("]");
   727         return sb.toString();
   728     }
   730     void toString(TreePath p, StringBuilder sb) {
   731         TreePath parent = p.getParentPath();
   732         if (parent != null) {
   733             toString(parent, sb);
   734             sb.append(",");
   735         }
   736        sb.append(p.getLeaf().getKind()).append(":").append(env.getPos(p)).append(":S").append(env.getStartPos(p));
   737     }
   739     void warnIfEmpty(DocTree tree, List<? extends DocTree> list) {
   740         for (DocTree d: list) {
   741             switch (d.getKind()) {
   742                 case TEXT:
   743                     if (!((TextTree) d).getBody().trim().isEmpty())
   744                         return;
   745                     break;
   746                 default:
   747                     return;
   748             }
   749         }
   750         env.messages.warning(SYNTAX, tree, "dc.empty", tree.getKind().tagName);
   751     }
   752     // </editor-fold>
   754 }

mercurial