[cfe-commits] r160078 - in /cfe/trunk: include/clang/AST/ include/clang/Basic/ include/clang/Sema/ lib/AST/ lib/Basic/ lib/Sema/ test/Sema/ tools/diagtool/ unittests/AST/

Dmitri Gribenko gribozavr at gmail.com
Wed Jul 11 14:38:40 PDT 2012


Author: gribozavr
Date: Wed Jul 11 16:38:39 2012
New Revision: 160078

URL: http://llvm.org/viewvc/llvm-project?rev=160078&view=rev
Log:
Enable comment parsing and semantic analysis to emit diagnostics.  A few
diagnostics implemented -- see testcases.

I created a new TableGen file for comment diagnostics,
DiagnosticCommentKinds.td, because comment diagnostics don't logically
fit into AST diagnostics file.  But I don't feel strongly about it.

This also implements support for self-closing HTML tags in comment
lexer and parser (for example, <br />).

In order to issue precise diagnostics CommentSema needs to know the
declaration the comment is attached to.  There is no easy way to find a decl by 
comment, so we match comments and decls in lockstep: after parsing one
declgroup we check if we have any new, not yet attached comments.  If we do --
then we do the usual comment-finding process.

It is interesting that this automatically handles trailing comments.
We pick up not only comments that precede the declaration, but also
comments that *follow* the declaration -- thanks to the lookahead in
the lexer: after parsing the declgroup we've consumed the semicolon
and looked ahead through comments.

Added -Wdocumentation-html flag for semantic HTML errors to allow the user to 
disable only HTML warnings (but not HTML parse errors, which we emit as
warnings in -Wdocumentation).


Added:
    cfe/trunk/include/clang/AST/CommentDiagnostic.h
    cfe/trunk/include/clang/Basic/DiagnosticCommentKinds.td
    cfe/trunk/test/Sema/warn-documentation-almost-trailing.c
      - copied, changed from r160070, cfe/trunk/test/Sema/doxygen-comments.c
    cfe/trunk/test/Sema/warn-documentation-fixits.c
    cfe/trunk/test/Sema/warn-documentation.cpp
    cfe/trunk/test/Sema/warn-documentation.m
Removed:
    cfe/trunk/test/Sema/doxygen-comments.c
Modified:
    cfe/trunk/include/clang/AST/ASTContext.h
    cfe/trunk/include/clang/AST/Comment.h
    cfe/trunk/include/clang/AST/CommentLexer.h
    cfe/trunk/include/clang/AST/CommentParser.h
    cfe/trunk/include/clang/AST/CommentSema.h
    cfe/trunk/include/clang/AST/RawCommentList.h
    cfe/trunk/include/clang/Basic/AllDiagnostics.h
    cfe/trunk/include/clang/Basic/CMakeLists.txt
    cfe/trunk/include/clang/Basic/Diagnostic.td
    cfe/trunk/include/clang/Basic/DiagnosticGroups.td
    cfe/trunk/include/clang/Basic/DiagnosticIDs.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Basic/Makefile
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/AST/ASTContext.cpp
    cfe/trunk/lib/AST/CMakeLists.txt
    cfe/trunk/lib/AST/Comment.cpp
    cfe/trunk/lib/AST/CommentDumper.cpp
    cfe/trunk/lib/AST/CommentLexer.cpp
    cfe/trunk/lib/AST/CommentParser.cpp
    cfe/trunk/lib/AST/CommentSema.cpp
    cfe/trunk/lib/AST/RawCommentList.cpp
    cfe/trunk/lib/Basic/DiagnosticIDs.cpp
    cfe/trunk/lib/Sema/CMakeLists.txt
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaDeclObjC.cpp
    cfe/trunk/tools/diagtool/DiagnosticNames.cpp
    cfe/trunk/unittests/AST/CommentLexer.cpp
    cfe/trunk/unittests/AST/CommentParser.cpp
    cfe/trunk/unittests/AST/Makefile

Modified: cfe/trunk/include/clang/AST/ASTContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTContext.h?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ASTContext.h (original)
+++ cfe/trunk/include/clang/AST/ASTContext.h Wed Jul 11 16:38:39 2012
@@ -392,6 +392,11 @@
   
   SourceManager& getSourceManager() { return SourceMgr; }
   const SourceManager& getSourceManager() const { return SourceMgr; }
+
+  llvm::BumpPtrAllocator &getAllocator() const {
+    return BumpAlloc;
+  }
+
   void *Allocate(unsigned Size, unsigned Align = 8) const {
     return BumpAlloc.Allocate(Size, Align);
   }
@@ -436,9 +441,13 @@
 
   /// \brief Return the documentation comment attached to a given declaration,
   /// without looking into cache.
-  const RawComment *getRawCommentForDeclNoCache(const Decl *D) const;
+  RawComment *getRawCommentForDeclNoCache(const Decl *D) const;
 
 public:
+  RawCommentList &getRawCommentList() {
+    return Comments;
+  }
+
   void addComment(const RawComment &RC) {
     Comments.addComment(RC, BumpAlloc);
   }

Modified: cfe/trunk/include/clang/AST/Comment.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Comment.h?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Comment.h (original)
+++ cfe/trunk/include/clang/AST/Comment.h Wed Jul 11 16:38:39 2012
@@ -50,6 +50,16 @@
   };
   enum { NumInlineContentCommentBitfields = 9 };
 
+  class HTMLOpenTagCommentBitfields {
+    friend class HTMLOpenTagComment;
+
+    unsigned : NumInlineContentCommentBitfields;
+
+    /// True if this tag is self-closing (e. g., <br />).  This is based on tag
+    /// spelling in comment (plain <br> would not set this flag).
+    unsigned IsSelfClosing : 1;
+  };
+
   class ParamCommandCommentBitfields {
     friend class ParamCommandComment;
 
@@ -66,6 +76,7 @@
   union {
     CommentBitfields CommentBits;
     InlineContentCommentBitfields InlineContentCommentBits;
+    HTMLOpenTagCommentBitfields HTMLOpenTagCommentBits;
     ParamCommandCommentBitfields ParamCommandCommentBits;
   };
 
@@ -107,8 +118,6 @@
 
   static bool classof(const Comment *) { return true; }
 
-  typedef Comment * const *child_iterator;
-
   SourceRange getSourceRange() const LLVM_READONLY { return Range; }
 
   SourceLocation getLocStart() const LLVM_READONLY {
@@ -121,9 +130,13 @@
 
   SourceLocation getLocation() const LLVM_READONLY { return Loc; }
 
+  typedef Comment * const *child_iterator;
+
   child_iterator child_begin() const;
   child_iterator child_end() const;
 
+  // TODO: const child iterator
+
   unsigned child_count() const {
     return child_end() - child_begin();
   }
@@ -180,6 +193,8 @@
   child_iterator child_end() const { return NULL; }
 
   StringRef getText() const LLVM_READONLY { return Text; }
+
+  bool isWhitespace() const;
 };
 
 /// A command with word-like arguments that is considered inline content.
@@ -325,8 +340,9 @@
                      LocBegin, LocBegin.getLocWithOffset(1 + TagName.size()),
                      TagName,
                      LocBegin.getLocWithOffset(1),
-                     LocBegin.getLocWithOffset(1 + TagName.size()))
-  { }
+                     LocBegin.getLocWithOffset(1 + TagName.size())) {
+    HTMLOpenTagCommentBits.IsSelfClosing = false;
+  }
 
   static bool classof(const Comment *C) {
     return C->getCommentKind() == HTMLOpenTagCommentKind;
@@ -362,6 +378,14 @@
   void setGreaterLoc(SourceLocation GreaterLoc) {
     Range.setEnd(GreaterLoc);
   }
+
+  bool isSelfClosing() const {
+    return HTMLOpenTagCommentBits.IsSelfClosing;
+  }
+
+  void setSelfClosing() {
+    HTMLOpenTagCommentBits.IsSelfClosing = true;
+  }
 };
 
 /// A closing HTML tag.
@@ -438,6 +462,8 @@
   child_iterator child_end() const {
     return reinterpret_cast<child_iterator>(Content.end());
   }
+
+  bool isWhitespace() const;
 };
 
 /// A command that has zero or more word-like arguments (number of word-like
@@ -520,6 +546,11 @@
 
   void setArgs(llvm::ArrayRef<Argument> A) {
     Args = A;
+    if (Args.size() > 0) {
+      SourceLocation NewLocEnd = Args.back().Range.getEnd();
+      if (NewLocEnd.isValid())
+        setSourceRange(SourceRange(getLocStart(), NewLocEnd));
+    }
   }
 
   ParagraphComment *getParagraph() const LLVM_READONLY {
@@ -536,18 +567,18 @@
 
 /// Doxygen \\param command.
 class ParamCommandComment : public BlockCommandComment {
-public:
-  enum PassDirection {
-    In,
-    Out,
-    InOut
-  };
+private:
+  /// Parameter index in the function declaration.
+  unsigned ParamIndex;
 
 public:
+  enum { InvalidParamIndex = ~0U };
+
   ParamCommandComment(SourceLocation LocBegin,
                       SourceLocation LocEnd,
                       StringRef Name) :
-      BlockCommandComment(ParamCommandCommentKind, LocBegin, LocEnd, Name) {
+      BlockCommandComment(ParamCommandCommentKind, LocBegin, LocEnd, Name),
+      ParamIndex(InvalidParamIndex) {
     ParamCommandCommentBits.Direction = In;
     ParamCommandCommentBits.IsDirectionExplicit = false;
   }
@@ -558,6 +589,14 @@
 
   static bool classof(const ParamCommandComment *) { return true; }
 
+  enum PassDirection {
+    In,
+    Out,
+    InOut
+  };
+
+  static const char *getDirectionAsString(PassDirection D);
+
   PassDirection getDirection() const LLVM_READONLY {
     return static_cast<PassDirection>(ParamCommandCommentBits.Direction);
   }
@@ -582,6 +621,19 @@
   SourceRange getParamNameRange() const {
     return Args[0].Range;
   }
+
+  bool isParamIndexValid() const LLVM_READONLY {
+    return ParamIndex != InvalidParamIndex;
+  }
+
+  unsigned getParamIndex() const LLVM_READONLY {
+    return ParamIndex;
+  }
+
+  void setParamIndex(unsigned Index) {
+    ParamIndex = Index;
+    assert(isParamIndexValid());
+  }
 };
 
 /// A line of text contained in a verbatim block.

Added: cfe/trunk/include/clang/AST/CommentDiagnostic.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/CommentDiagnostic.h?rev=160078&view=auto
==============================================================================
--- cfe/trunk/include/clang/AST/CommentDiagnostic.h (added)
+++ cfe/trunk/include/clang/AST/CommentDiagnostic.h Wed Jul 11 16:38:39 2012
@@ -0,0 +1,29 @@
+//===--- CommentDiagnostic.h - Diagnostics for the AST library --*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_COMMENTDIAGNOSTIC_H
+#define LLVM_CLANG_COMMENTDIAGNOSTIC_H
+
+#include "clang/Basic/Diagnostic.h"
+
+namespace clang {
+  namespace diag {
+    enum {
+#define DIAG(ENUM,FLAGS,DEFAULT_MAPPING,DESC,GROUP,\
+             SFINAE,ACCESS,NOWERROR,SHOWINSYSHEADER,CATEGORY) ENUM,
+#define COMMENTSTART
+#include "clang/Basic/DiagnosticCommentKinds.inc"
+#undef DIAG
+      NUM_BUILTIN_COMMENT_DIAGNOSTICS
+    };
+  }  // end namespace diag
+}  // end namespace clang
+
+#endif
+

Modified: cfe/trunk/include/clang/AST/CommentLexer.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/CommentLexer.h?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/CommentLexer.h (original)
+++ cfe/trunk/include/clang/AST/CommentLexer.h Wed Jul 11 16:38:39 2012
@@ -43,6 +43,7 @@
   html_equals,        // =
   html_quoted_string, // "blah\"blah" or 'blah\'blah'
   html_greater,       // >
+  html_slash_greater, // />
   html_tag_close      // </tag
 };
 } // end namespace tok

Modified: cfe/trunk/include/clang/AST/CommentParser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/CommentParser.h?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/CommentParser.h (original)
+++ cfe/trunk/include/clang/AST/CommentParser.h Wed Jul 11 16:38:39 2012
@@ -14,12 +14,15 @@
 #ifndef LLVM_CLANG_AST_COMMENT_PARSER_H
 #define LLVM_CLANG_AST_COMMENT_PARSER_H
 
+#include "clang/Basic/Diagnostic.h"
 #include "clang/AST/CommentLexer.h"
 #include "clang/AST/Comment.h"
 #include "clang/AST/CommentSema.h"
 #include "llvm/Support/Allocator.h"
 
 namespace clang {
+class SourceManager;
+
 namespace comments {
 
 /// Doxygen comment parser.
@@ -28,8 +31,12 @@
 
   Sema &S;
 
+  /// Allocator for anything that goes into AST nodes.
   llvm::BumpPtrAllocator &Allocator;
 
+  /// Source manager for the comment being parsed.
+  const SourceManager &SourceMgr;
+
   template<typename T>
   ArrayRef<T> copyArray(ArrayRef<T> Source) {
     size_t Size = Source.size();
@@ -41,6 +48,12 @@
       return llvm::makeArrayRef(static_cast<T *>(NULL), 0);
   }
 
+  DiagnosticsEngine &Diags;
+
+  DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) {
+    return Diags.Report(Loc, DiagID);
+  }
+
   /// Current lookahead token.  We can safely assume that all tokens are from
   /// a single source file.
   Token Tok;
@@ -79,7 +92,8 @@
   }
 
 public:
-  Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator);
+  Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator,
+         const SourceManager &SourceMgr, DiagnosticsEngine &Diags);
 
   /// Parse arguments for \\param command.
   ParamCommandComment *parseParamCommandArgs(

Modified: cfe/trunk/include/clang/AST/CommentSema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/CommentSema.h?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/CommentSema.h (original)
+++ cfe/trunk/include/clang/AST/CommentSema.h Wed Jul 11 16:38:39 2012
@@ -14,6 +14,7 @@
 #ifndef LLVM_CLANG_AST_COMMENT_SEMA_H
 #define LLVM_CLANG_AST_COMMENT_SEMA_H
 
+#include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/AST/Comment.h"
 #include "llvm/ADT/ArrayRef.h"
@@ -21,13 +22,37 @@
 #include "llvm/Support/Allocator.h"
 
 namespace clang {
+class Decl;
+class FunctionDecl;
+class ParmVarDecl;
+class SourceMgr;
+
 namespace comments {
 
 class Sema {
+  /// Allocator for AST nodes.
   llvm::BumpPtrAllocator &Allocator;
 
+  /// Source manager for the comment being parsed.
+  const SourceManager &SourceMgr;
+
+  DiagnosticsEngine &Diags;
+
+  const Decl *ThisDecl;
+
+  DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) {
+    return Diags.Report(Loc, DiagID);
+  }
+
+  /// A stack of HTML tags that are currently open (not matched with closing
+  /// tags).
+  SmallVector<HTMLOpenTagComment *, 8> HTMLOpenTags;
+
 public:
-  Sema(llvm::BumpPtrAllocator &Allocator);
+  Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
+       DiagnosticsEngine &Diags);
+
+  void setDecl(const Decl *D);
 
   ParagraphComment *actOnParagraphComment(
       ArrayRef<InlineContentComment *> Content);
@@ -47,11 +72,17 @@
                                               SourceLocation LocEnd,
                                               StringRef Name);
 
-  ParamCommandComment *actOnParamCommandArg(ParamCommandComment *Command,
+  ParamCommandComment *actOnParamCommandDirectionArg(
+                                            ParamCommandComment *Command,
+                                            SourceLocation ArgLocBegin,
+                                            SourceLocation ArgLocEnd,
+                                            StringRef Arg);
+
+  ParamCommandComment *actOnParamCommandParamNameArg(
+                                            ParamCommandComment *Command,
                                             SourceLocation ArgLocBegin,
                                             SourceLocation ArgLocEnd,
-                                            StringRef Arg,
-                                            bool IsDirection);
+                                            StringRef Arg);
 
   ParamCommandComment *actOnParamCommandFinish(ParamCommandComment *Command,
                                                ParagraphComment *Paragraph);
@@ -98,7 +129,8 @@
   HTMLOpenTagComment *actOnHTMLOpenTagFinish(
                               HTMLOpenTagComment *Tag,
                               ArrayRef<HTMLOpenTagComment::Attribute> Attrs,
-                              SourceLocation GreaterLoc);
+                              SourceLocation GreaterLoc,
+                              bool IsSelfClosing);
 
   HTMLCloseTagComment *actOnHTMLCloseTag(SourceLocation LocBegin,
                                          SourceLocation LocEnd,
@@ -106,6 +138,19 @@
 
   FullComment *actOnFullComment(ArrayRef<BlockContentComment *> Blocks);
 
+  void checkBlockCommandEmptyParagraph(BlockCommandComment *Command);
+
+  /// Returns index of a function parameter with a given name.
+  unsigned resolveParmVarReference(StringRef Name,
+                                   const ParmVarDecl * const *ParamVars,
+                                   unsigned NumParams);
+
+  /// Returns index of a function parameter with the name closest to a given
+  /// typo.
+  unsigned correctTypoInParmVarReference(StringRef Typo,
+                                         const ParmVarDecl * const *ParamVars,
+                                         unsigned NumParams);
+
   bool isBlockCommand(StringRef Name);
   bool isParamCommand(StringRef Name);
   unsigned getBlockCommandNumArgs(StringRef Name);

Modified: cfe/trunk/include/clang/AST/RawCommentList.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/RawCommentList.h?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/RawCommentList.h (original)
+++ cfe/trunk/include/clang/AST/RawCommentList.h Wed Jul 11 16:38:39 2012
@@ -48,6 +48,14 @@
     return Kind == RCK_Merged;
   }
 
+  bool isAttached() const LLVM_READONLY {
+    return IsAttached;
+  }
+
+  void setAttached() {
+    IsAttached = true;
+  }
+
   /// Returns true if it is a comment that should be put after a member:
   /// \code ///< stuff \endcode
   /// \code //!< stuff \endcode
@@ -110,6 +118,9 @@
 
   unsigned Kind : 3;
 
+  /// True if comment is attached to a declaration in ASTContext.
+  bool IsAttached : 1;
+
   bool IsTrailingComment : 1;
   bool IsAlmostTrailingComment : 1;
 
@@ -122,7 +133,7 @@
   RawComment(SourceRange SR, CommentKind K, bool IsTrailingComment,
              bool IsAlmostTrailingComment) :
     Range(SR), RawTextValid(false), BriefTextValid(false), Kind(K),
-    IsTrailingComment(IsTrailingComment),
+    IsAttached(false), IsTrailingComment(IsTrailingComment),
     IsAlmostTrailingComment(IsAlmostTrailingComment),
     BeginLineValid(false), EndLineValid(false)
   { }

Modified: cfe/trunk/include/clang/Basic/AllDiagnostics.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/AllDiagnostics.h?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/AllDiagnostics.h (original)
+++ cfe/trunk/include/clang/Basic/AllDiagnostics.h Wed Jul 11 16:38:39 2012
@@ -16,6 +16,7 @@
 #define LLVM_CLANG_ALL_DIAGNOSTICS_H
 
 #include "clang/AST/ASTDiagnostic.h"
+#include "clang/AST/CommentDiagnostic.h"
 #include "clang/Analysis/AnalysisDiagnostic.h"
 #include "clang/Driver/DriverDiagnostic.h"
 #include "clang/Frontend/FrontendDiagnostic.h"

Modified: cfe/trunk/include/clang/Basic/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/CMakeLists.txt?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/CMakeLists.txt (original)
+++ cfe/trunk/include/clang/Basic/CMakeLists.txt Wed Jul 11 16:38:39 2012
@@ -7,6 +7,7 @@
 
 clang_diag_gen(Analysis)
 clang_diag_gen(AST)
+clang_diag_gen(Comment)
 clang_diag_gen(Common)
 clang_diag_gen(Driver)
 clang_diag_gen(Frontend)

Modified: cfe/trunk/include/clang/Basic/Diagnostic.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Diagnostic.td?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Diagnostic.td (original)
+++ cfe/trunk/include/clang/Basic/Diagnostic.td Wed Jul 11 16:38:39 2012
@@ -88,6 +88,7 @@
 // Definitions for Diagnostics.
 include "DiagnosticASTKinds.td"
 include "DiagnosticAnalysisKinds.td"
+include "DiagnosticCommentKinds.td"
 include "DiagnosticCommonKinds.td"
 include "DiagnosticDriverKinds.td"
 include "DiagnosticFrontendKinds.td"

Added: cfe/trunk/include/clang/Basic/DiagnosticCommentKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticCommentKinds.td?rev=160078&view=auto
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticCommentKinds.td (added)
+++ cfe/trunk/include/clang/Basic/DiagnosticCommentKinds.td Wed Jul 11 16:38:39 2012
@@ -0,0 +1,70 @@
+//==--- DiagnosticCommentKinds.td - diagnostics related to comments -------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+let Component = "Comment" in {
+let CategoryName = "Documentation Issue" in {
+
+// HTML parsing errors.  These are under -Wdocumentation to make sure the user
+// knows that we didn't parse something as he might expect.
+
+def warn_doc_html_open_tag_expected_quoted_string : Warning<
+  "expected quoted string after equals sign">,
+  InGroup<Documentation>, DefaultIgnore;
+
+def warn_doc_html_open_tag_expected_ident_or_greater : Warning<
+  "HTML opening tag prematurely ended, expected attribute name or '>'">,
+  InGroup<Documentation>, DefaultIgnore;
+
+def note_doc_html_tag_started_here : Note<
+  "HTML tag started here">;
+
+// HTML semantic errors
+
+def warn_doc_html_close_unbalanced : Warning<
+  "HTML closing tag does not match any opening tag">,
+  InGroup<DocumentationHTML>, DefaultIgnore;
+
+def warn_doc_html_open_close_mismatch : Warning<
+  "HTML opening tag '%0' closed by '%1'">,
+  InGroup<DocumentationHTML>, DefaultIgnore;
+
+def note_doc_html_closing_tag : Note<
+  "closing tag">;
+
+// Commands
+
+def warn_doc_block_command_empty_paragraph : Warning<
+  "empty paragraph passed to '\\%0' command">,
+  InGroup<Documentation>, DefaultIgnore;
+
+// \param command
+
+def warn_doc_param_invalid_direction : Warning<
+  "unrecognized parameter passing direction, "
+  "valid directions are '[in]', '[out]' and '[in,out]'">,
+  InGroup<Documentation>, DefaultIgnore;
+
+def warn_doc_param_spaces_in_direction : Warning<
+  "whitespace is not allowed in parameter passing direction">,
+  InGroup<DocumentationPedantic>, DefaultIgnore;
+
+def warn_doc_param_not_attached_to_a_function_decl : Warning<
+  "'\\param' command used in a comment that is not attached to "
+  "a function declaration">,
+  InGroup<Documentation>, DefaultIgnore;
+
+def warn_doc_param_not_found : Warning<
+  "parameter '%0' not found in the function declaration">,
+  InGroup<Documentation>, DefaultIgnore;
+
+def note_doc_param_name_suggestion : Note<
+  "did you mean '%0'?">;
+
+} // end of documentation issue category
+} // end of AST component

Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Wed Jul 11 16:38:39 2012
@@ -57,7 +57,9 @@
 def : DiagGroup<"disabled-optimization">;
 def : DiagGroup<"discard-qual">;
 def : DiagGroup<"div-by-zero">;
-def Doxygen : DiagGroup<"doxygen">;
+def DocumentationHTML : DiagGroup<"documentation-html">;
+def DocumentationPedantic : DiagGroup<"documentation-pedantic">;
+def Documentation : DiagGroup<"documentation", [DocumentationHTML]>;
 def EmptyBody : DiagGroup<"empty-body">;
 def ExtraTokens : DiagGroup<"extra-tokens">;
 

Modified: cfe/trunk/include/clang/Basic/DiagnosticIDs.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticIDs.h?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticIDs.h (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticIDs.h Wed Jul 11 16:38:39 2012
@@ -38,7 +38,8 @@
       DIAG_START_LEX           = DIAG_START_SERIALIZATION   +  120,
       DIAG_START_PARSE         = DIAG_START_LEX             +  300,
       DIAG_START_AST           = DIAG_START_PARSE           +  400,
-      DIAG_START_SEMA          = DIAG_START_AST             +  100,
+      DIAG_START_COMMENT       = DIAG_START_AST             +  100,
+      DIAG_START_SEMA          = DIAG_START_COMMENT         +  100,
       DIAG_START_ANALYSIS      = DIAG_START_SEMA            + 3000,
       DIAG_UPPER_LIMIT         = DIAG_START_ANALYSIS        +  100
     };

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Jul 11 16:38:39 2012
@@ -5704,7 +5704,7 @@
 
 let CategoryName = "Documentation Issue" in {
 def warn_not_a_doxygen_trailing_member_comment : Warning<
-  "not a Doxygen trailing comment">, InGroup<Doxygen>, DefaultIgnore;
+  "not a Doxygen trailing comment">, InGroup<Documentation>, DefaultIgnore;
 } // end of documentation issue category
 
 } // end of sema component.

Modified: cfe/trunk/include/clang/Basic/Makefile
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Makefile?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Makefile (original)
+++ cfe/trunk/include/clang/Basic/Makefile Wed Jul 11 16:38:39 2012
@@ -1,6 +1,7 @@
 CLANG_LEVEL := ../../..
 BUILT_SOURCES = \
 	DiagnosticAnalysisKinds.inc DiagnosticASTKinds.inc \
+	DiagnosticCommentKinds.inc \
 	DiagnosticCommonKinds.inc DiagnosticDriverKinds.inc \
 	DiagnosticFrontendKinds.inc DiagnosticLexKinds.inc \
 	DiagnosticParseKinds.inc DiagnosticSemaKinds.inc \

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Wed Jul 11 16:38:39 2012
@@ -1313,6 +1313,12 @@
                                          unsigned NumDecls);
   DeclGroupPtrTy BuildDeclaratorGroup(Decl **Group, unsigned NumDecls,
                                       bool TypeMayContainAuto = true);
+
+  /// Should be called on all declarations that might have attached
+  /// documentation comments.
+  void ActOnDocumentableDecl(Decl *D);
+  void ActOnDocumentableDecls(Decl **Group, unsigned NumDecls);
+
   void ActOnFinishKNRParamDeclarations(Scope *S, Declarator &D,
                                        SourceLocation LocAfterDecls);
   void CheckForFunctionRedefinition(FunctionDecl *FD);

Modified: cfe/trunk/lib/AST/ASTContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTContext.cpp (original)
+++ cfe/trunk/lib/AST/ASTContext.cpp Wed Jul 11 16:38:39 2012
@@ -56,7 +56,7 @@
   HalfRank, FloatRank, DoubleRank, LongDoubleRank
 };
 
-const RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const {
+RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const {
   if (!CommentsLoaded && ExternalSource) {
     ExternalSource->ReadComments();
     CommentsLoaded = true;
@@ -160,11 +160,13 @@
     return C.first;
   }
 
-  const RawComment *RC = getRawCommentForDeclNoCache(D);
+  RawComment *RC = getRawCommentForDeclNoCache(D);
   // If we found a comment, it should be a documentation comment.
   assert(!RC || RC->isDocumentation());
   DeclComments[D] =
       RawAndParsedComment(RC, static_cast<comments::FullComment *>(NULL));
+  if (RC)
+    RC->setAttached();
   return RC;
 }
 
@@ -187,8 +189,10 @@
   comments::Lexer L(RC->getSourceRange().getBegin(), comments::CommentOptions(),
                     RawText.begin(), RawText.end());
 
-  comments::Sema S(this->BumpAlloc);
-  comments::Parser P(L, S, this->BumpAlloc);
+  comments::Sema S(getAllocator(), getSourceManager(), getDiagnostics());
+  S.setDecl(D);
+  comments::Parser P(L, S, getAllocator(), getSourceManager(),
+                     getDiagnostics());
 
   comments::FullComment *FC = P.parseFullComment();
   DeclComments[D].second = FC;

Modified: cfe/trunk/lib/AST/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/CMakeLists.txt?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/lib/AST/CMakeLists.txt (original)
+++ cfe/trunk/lib/AST/CMakeLists.txt Wed Jul 11 16:38:39 2012
@@ -64,6 +64,7 @@
   ClangAttrList
   ClangAttrImpl
   ClangDiagnosticAST
+  ClangDiagnosticComment
   ClangCommentNodes
   ClangDeclNodes
   ClangStmtNodes

Modified: cfe/trunk/lib/AST/Comment.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Comment.cpp?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Comment.cpp (original)
+++ cfe/trunk/lib/AST/Comment.cpp Wed Jul 11 16:38:39 2012
@@ -86,6 +86,38 @@
   llvm_unreachable("Unknown comment kind!");
 }
 
+bool TextComment::isWhitespace() const {
+  for (StringRef::const_iterator I = Text.begin(), E = Text.end();
+       I != E; ++I) {
+    const char C = *I;
+    if (C != ' ' && C != '\n' && C != '\r' &&
+        C != '\t' && C != '\f' && C != '\v')
+      return false;
+  }
+  return true;
+}
+
+bool ParagraphComment::isWhitespace() const {
+  for (child_iterator I = child_begin(), E = child_end(); I != E; ++I) {
+    if (const TextComment *TC = dyn_cast<TextComment>(*I)) {
+      if (!TC->isWhitespace())
+        return false;
+    }
+  }
+  return true;
+}
+
+const char *ParamCommandComment::getDirectionAsString(PassDirection D) {
+  switch (D) {
+  case ParamCommandComment::In:
+    return "[in]";
+  case ParamCommandComment::Out:
+    return "[out]";
+  case ParamCommandComment::InOut:
+    return "[in,out]";
+  }
+  llvm_unreachable("unknown PassDirection");
+}
 
 } // end namespace comments
 } // end namespace clang

Modified: cfe/trunk/lib/AST/CommentDumper.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/CommentDumper.cpp?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/lib/AST/CommentDumper.cpp (original)
+++ cfe/trunk/lib/AST/CommentDumper.cpp Wed Jul 11 16:38:39 2012
@@ -121,6 +121,8 @@
       OS << " \"" << Attr.Name << "=\"" << Attr.Value << "\"";
     }
   }
+  if (C->isSelfClosing())
+    OS << " SelfClosing";
 }
 
 void CommentDumper::visitHTMLCloseTagComment(const HTMLCloseTagComment *C) {
@@ -142,17 +144,7 @@
 void CommentDumper::visitParamCommandComment(const ParamCommandComment *C) {
   dumpComment(C);
 
-  switch (C->getDirection()) {
-  case ParamCommandComment::In:
-    OS << " [in]";
-    break;
-  case ParamCommandComment::Out:
-    OS << " [out]";
-    break;
-  case ParamCommandComment::InOut:
-    OS << " [in,out]";
-    break;
-  }
+  OS << " " << ParamCommandComment::getDirectionAsString(C->getDirection());
 
   if (C->isDirectionExplicit())
     OS << " explicitly";

Modified: cfe/trunk/lib/AST/CommentLexer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/CommentLexer.cpp?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/lib/AST/CommentLexer.cpp (original)
+++ cfe/trunk/lib/AST/CommentLexer.cpp Wed Jul 11 16:38:39 2012
@@ -509,7 +509,7 @@
 
   const char C = *BufferPtr;
   if (BufferPtr != CommentEnd &&
-      (C == '>' || isHTMLIdentifierStartingCharacter(C)))
+      (C == '>' || C == '/' || isHTMLIdentifierStartingCharacter(C)))
     State = LS_HTMLOpenTag;
 }
 
@@ -546,6 +546,18 @@
       formTokenWithChars(T, TokenPtr, tok::html_greater);
       State = LS_Normal;
       return;
+    case '/':
+      TokenPtr++;
+      if (TokenPtr != CommentEnd && *TokenPtr == '>') {
+        TokenPtr++;
+        formTokenWithChars(T, TokenPtr, tok::html_slash_greater);
+      } else {
+        StringRef Text(BufferPtr, TokenPtr - BufferPtr);
+        formTokenWithChars(T, TokenPtr, tok::text);
+        T.setText(Text);
+      }
+      State = LS_Normal;
+      return;
     }
   }
 

Modified: cfe/trunk/lib/AST/CommentParser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/CommentParser.cpp?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/lib/AST/CommentParser.cpp (original)
+++ cfe/trunk/lib/AST/CommentParser.cpp Wed Jul 11 16:38:39 2012
@@ -9,13 +9,16 @@
 
 #include "clang/AST/CommentParser.h"
 #include "clang/AST/CommentSema.h"
+#include "clang/AST/CommentDiagnostic.h"
+#include "clang/Basic/SourceManager.h"
 #include "llvm/Support/ErrorHandling.h"
 
 namespace clang {
 namespace comments {
 
-Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator):
-    L(L), S(S), Allocator(Allocator) {
+Parser::Parser(Lexer &L, Sema &S, llvm::BumpPtrAllocator &Allocator,
+               const SourceManager &SourceMgr, DiagnosticsEngine &Diags):
+    L(L), S(S), Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags) {
   consumeToken();
 }
 
@@ -26,18 +29,16 @@
   // Check if argument looks like direction specification: [dir]
   // e.g., [in], [out], [in,out]
   if (Retokenizer.lexDelimitedSeq(Arg, '[', ']'))
-    PC = S.actOnParamCommandArg(PC,
-                                Arg.getLocation(),
-                                Arg.getEndLocation(),
-                                Arg.getText(),
-                                /* IsDirection = */ true);
+    PC = S.actOnParamCommandDirectionArg(PC,
+                                         Arg.getLocation(),
+                                         Arg.getEndLocation(),
+                                         Arg.getText());
 
   if (Retokenizer.lexWord(Arg))
-    PC = S.actOnParamCommandArg(PC,
-                                Arg.getLocation(),
-                                Arg.getEndLocation(),
-                                Arg.getText(),
-                                /* IsDirection = */ false);
+    PC = S.actOnParamCommandParamNameArg(PC,
+                                         Arg.getLocation(),
+                                         Arg.getEndLocation(),
+                                         Arg.getText());
 
   return PC;
 }
@@ -84,7 +85,6 @@
   if (Tok.is(tok::command) && S.isBlockCommand(Tok.getCommandName())) {
     // Block command ahead.  We can't nest block commands, so pretend that this
     // command has an empty argument.
-    // TODO: Diag() Warn empty arg to block command
     ParagraphComment *PC = S.actOnParagraphComment(
                                 ArrayRef<InlineContentComment *>());
     return S.actOnBlockCommandFinish(BC, PC);
@@ -164,7 +164,8 @@
 
   SmallVector<HTMLOpenTagComment::Attribute, 2> Attrs;
   while (true) {
-    if (Tok.is(tok::html_ident)) {
+    switch (Tok.getKind()) {
+    case tok::html_ident: {
       Token Ident = Tok;
       consumeToken();
       if (Tok.isNot(tok::html_equals)) {
@@ -175,9 +176,14 @@
       Token Equals = Tok;
       consumeToken();
       if (Tok.isNot(tok::html_quoted_string)) {
-        // TODO: Diag() expected quoted string
+        Diag(Tok.getLocation(),
+             diag::warn_doc_html_open_tag_expected_quoted_string)
+          << SourceRange(Equals.getLocation());
         Attrs.push_back(HTMLOpenTagComment::Attribute(Ident.getLocation(),
                                                       Ident.getHTMLIdent()));
+        while (Tok.is(tok::html_equals) ||
+               Tok.is(tok::html_quoted_string))
+          consumeToken();
         continue;
       }
       Attrs.push_back(HTMLOpenTagComment::Attribute(
@@ -189,24 +195,66 @@
                               Tok.getHTMLQuotedString()));
       consumeToken();
       continue;
-    } else if (Tok.is(tok::html_greater)) {
+    }
+
+    case tok::html_greater:
+      HOT = S.actOnHTMLOpenTagFinish(HOT,
+                                     copyArray(llvm::makeArrayRef(Attrs)),
+                                     Tok.getLocation(),
+                                     /* IsSelfClosing = */ false);
+      consumeToken();
+      return HOT;
+
+    case tok::html_slash_greater:
       HOT = S.actOnHTMLOpenTagFinish(HOT,
                                      copyArray(llvm::makeArrayRef(Attrs)),
-                                     Tok.getLocation());
+                                     Tok.getLocation(),
+                                     /* IsSelfClosing = */ true);
       consumeToken();
       return HOT;
-    } else if (Tok.is(tok::html_equals) ||
-               Tok.is(tok::html_quoted_string)) {
-      // TODO: Diag() Err expected ident
+
+    case tok::html_equals:
+    case tok::html_quoted_string:
+      Diag(Tok.getLocation(),
+           diag::warn_doc_html_open_tag_expected_ident_or_greater);
       while (Tok.is(tok::html_equals) ||
              Tok.is(tok::html_quoted_string))
         consumeToken();
-    } else {
-      // Not a token from HTML open tag.  Thus HTML tag prematurely ended.
-      // TODO: Diag() Err HTML tag prematurely ended
+      if (Tok.is(tok::html_ident) ||
+          Tok.is(tok::html_greater) ||
+          Tok.is(tok::html_slash_greater))
+        continue;
+
       return S.actOnHTMLOpenTagFinish(HOT,
                                       copyArray(llvm::makeArrayRef(Attrs)),
-                                      SourceLocation());
+                                      SourceLocation(),
+                                      /* IsSelfClosing = */ false);
+
+    default:
+      // Not a token from an HTML open tag.  Thus HTML tag prematurely ended.
+      HOT = S.actOnHTMLOpenTagFinish(HOT,
+                                     copyArray(llvm::makeArrayRef(Attrs)),
+                                     SourceLocation(),
+                                     /* IsSelfClosing = */ false);
+      bool StartLineInvalid;
+      const unsigned StartLine = SourceMgr.getPresumedLineNumber(
+                                                  HOT->getLocation(),
+                                                  &StartLineInvalid);
+      bool EndLineInvalid;
+      const unsigned EndLine = SourceMgr.getPresumedLineNumber(
+                                                  Tok.getLocation(),
+                                                  &EndLineInvalid);
+      if (StartLineInvalid || EndLineInvalid || StartLine == EndLine)
+        Diag(Tok.getLocation(),
+             diag::warn_doc_html_open_tag_expected_ident_or_greater)
+          << HOT->getSourceRange();
+      else {
+        Diag(Tok.getLocation(),
+             diag::warn_doc_html_open_tag_expected_ident_or_greater);
+        Diag(HOT->getLocation(), diag::note_doc_html_tag_started_here)
+          << HOT->getSourceRange();
+      }
+      return HOT;
     }
   }
 }
@@ -289,6 +337,7 @@
     case tok::html_equals:
     case tok::html_quoted_string:
     case tok::html_greater:
+    case tok::html_slash_greater:
       llvm_unreachable("should not see this token");
     }
     break;
@@ -388,6 +437,7 @@
   case tok::html_equals:
   case tok::html_quoted_string:
   case tok::html_greater:
+  case tok::html_slash_greater:
     llvm_unreachable("should not see this token");
   }
   llvm_unreachable("bogus token kind");

Modified: cfe/trunk/lib/AST/CommentSema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/CommentSema.cpp?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/lib/AST/CommentSema.cpp (original)
+++ cfe/trunk/lib/AST/CommentSema.cpp Wed Jul 11 16:38:39 2012
@@ -8,13 +8,22 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/AST/CommentSema.h"
+#include "clang/AST/CommentDiagnostic.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/Basic/SourceManager.h"
 #include "llvm/ADT/StringSwitch.h"
 
 namespace clang {
 namespace comments {
 
-Sema::Sema(llvm::BumpPtrAllocator &Allocator) :
-    Allocator(Allocator) {
+Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
+           DiagnosticsEngine &Diags) :
+    Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), ThisDecl(NULL) {
+}
+
+void Sema::setDecl(const Decl *D) {
+  ThisDecl = D;
 }
 
 ParagraphComment *Sema::actOnParagraphComment(
@@ -39,83 +48,153 @@
                               BlockCommandComment *Command,
                               ParagraphComment *Paragraph) {
   Command->setParagraph(Paragraph);
+  checkBlockCommandEmptyParagraph(Command);
   return Command;
 }
 
 ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
                                                   SourceLocation LocEnd,
                                                   StringRef Name) {
-  return new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
+  ParamCommandComment *Command =
+      new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
+
+  if (!ThisDecl ||
+      !(isa<FunctionDecl>(ThisDecl) || isa<ObjCMethodDecl>(ThisDecl)))
+    Diag(Command->getLocation(),
+         diag::warn_doc_param_not_attached_to_a_function_decl)
+      << Command->getCommandNameRange();
+
+  return Command;
 }
 
-ParamCommandComment *Sema::actOnParamCommandArg(ParamCommandComment *Command,
+ParamCommandComment *Sema::actOnParamCommandDirectionArg(
+                                                ParamCommandComment *Command,
                                                 SourceLocation ArgLocBegin,
                                                 SourceLocation ArgLocEnd,
-                                                StringRef Arg,
-                                                bool IsDirection) {
-  if (IsDirection) {
-    ParamCommandComment::PassDirection Direction;
-    std::string ArgLower = Arg.lower();
-    // TODO: optimize: lower Name first (need an API in SmallString for that),
-    // after that StringSwitch.
-    if (ArgLower == "[in]")
+                                                StringRef Arg) {
+  ParamCommandComment::PassDirection Direction;
+  std::string ArgLower = Arg.lower();
+  // TODO: optimize: lower Name first (need an API in SmallString for that),
+  // after that StringSwitch.
+  if (ArgLower == "[in]")
+    Direction = ParamCommandComment::In;
+  else if (ArgLower == "[out]")
+    Direction = ParamCommandComment::Out;
+  else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
+    Direction = ParamCommandComment::InOut;
+  else {
+    // Remove spaces.
+    std::string::iterator O = ArgLower.begin();
+    for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
+         I != E; ++I) {
+      const char C = *I;
+      if (C != ' ' && C != '\n' && C != '\r' &&
+          C != '\t' && C != '\v' && C != '\f')
+        *O++ = C;
+    }
+    ArgLower.resize(O - ArgLower.begin());
+
+    bool RemovingWhitespaceHelped = false;
+    if (ArgLower == "[in]") {
       Direction = ParamCommandComment::In;
-    else if (ArgLower == "[out]")
+      RemovingWhitespaceHelped = true;
+    } else if (ArgLower == "[out]") {
       Direction = ParamCommandComment::Out;
-    else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
+      RemovingWhitespaceHelped = true;
+    } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
       Direction = ParamCommandComment::InOut;
-    else {
-      // Remove spaces.
-      std::string::iterator O = ArgLower.begin();
-      for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
-           I != E; ++I) {
-        const char C = *I;
-        if (C != ' ' && C != '\n' && C != '\r' &&
-            C != '\t' && C != '\v' && C != '\f')
-          *O++ = C;
-      }
-      ArgLower.resize(O - ArgLower.begin());
-
-      bool RemovingWhitespaceHelped = false;
-      if (ArgLower == "[in]") {
-        Direction = ParamCommandComment::In;
-        RemovingWhitespaceHelped = true;
-      } else if (ArgLower == "[out]") {
-        Direction = ParamCommandComment::Out;
-        RemovingWhitespaceHelped = true;
-      } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
-        Direction = ParamCommandComment::InOut;
-        RemovingWhitespaceHelped = true;
-      } else {
-        Direction = ParamCommandComment::In;
-        RemovingWhitespaceHelped = false;
-      }
-      // Diag() unrecognized parameter passing direction, valid directions are ...
-      // if (RemovingWhitespaceHelped) FixIt
-    }
-    Command->setDirection(Direction, /* Explicit = */ true);
-  } else {
-    if (Command->getArgCount() == 0) {
-      if (!Command->isDirectionExplicit()) {
-        // User didn't provide a direction argument.
-        Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
-      }
-      typedef BlockCommandComment::Argument Argument;
-      Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
-                                                         ArgLocEnd),
-                                             Arg);
-      Command->setArgs(llvm::makeArrayRef(A, 1));
-      // if (...) Diag() unrecognized parameter name
+      RemovingWhitespaceHelped = true;
     } else {
-      // Diag() \\param command requires at most 2 arguments
+      Direction = ParamCommandComment::In;
+      RemovingWhitespaceHelped = false;
     }
+
+    SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
+    if (RemovingWhitespaceHelped)
+      Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
+        << ArgRange
+        << FixItHint::CreateReplacement(
+                          ArgRange,
+                          ParamCommandComment::getDirectionAsString(Direction));
+    else
+      Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
+        << ArgRange;
+  }
+  Command->setDirection(Direction, /* Explicit = */ true);
+  return Command;
+}
+
+ParamCommandComment *Sema::actOnParamCommandParamNameArg(
+                                                ParamCommandComment *Command,
+                                                SourceLocation ArgLocBegin,
+                                                SourceLocation ArgLocEnd,
+                                                StringRef Arg) {
+  // Parser will not feed us more arguments than needed.
+  assert(Command->getArgCount() == 0);
+
+  if (!Command->isDirectionExplicit()) {
+    // User didn't provide a direction argument.
+    Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
+  }
+  typedef BlockCommandComment::Argument Argument;
+  Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
+                                                     ArgLocEnd),
+                                         Arg);
+  Command->setArgs(llvm::makeArrayRef(A, 1));
+
+  if (!ThisDecl)
+    return Command;
+
+  const ParmVarDecl * const *ParamVars;
+  unsigned NumParams;
+  if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ThisDecl)) {
+    ParamVars = FD->param_begin();
+    NumParams = FD->getNumParams();
+  } else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ThisDecl)) {
+    ParamVars = MD->param_begin();
+    NumParams = MD->param_size();
+  } else {
+    // We already warned that this \\param is not attached to a function decl.
+    return Command;
+  }
+
+  // Check that referenced parameter name is in the function decl.
+  const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars,
+                                                              NumParams);
+  if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
+    Command->setParamIndex(ResolvedParamIndex);
+    return Command;
+  }
+
+  SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
+  Diag(ArgLocBegin, diag::warn_doc_param_not_found)
+    << Arg << ArgRange;
+
+  unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
+  if (NumParams == 1) {
+    // If function has only one parameter then only that parameter
+    // can be documented.
+    CorrectedParamIndex = 0;
+  } else {
+    // Do typo correction.
+    CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars,
+                                                        NumParams);
+  }
+  if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
+    const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex];
+    if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
+      Diag(ArgLocBegin, diag::note_doc_param_name_suggestion)
+        << CorrectedII->getName()
+        << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
   }
+
   return Command;
 }
 
 ParamCommandComment *Sema::actOnParamCommandFinish(ParamCommandComment *Command,
                                                    ParagraphComment *Paragraph) {
   Command->setParagraph(Paragraph);
+  checkBlockCommandEmptyParagraph(Command);
   return Command;
 }
 
@@ -196,22 +275,78 @@
 
 HTMLOpenTagComment *Sema::actOnHTMLOpenTagStart(SourceLocation LocBegin,
                                                 StringRef TagName) {
-  return new (Allocator) HTMLOpenTagComment(LocBegin, TagName);
+  HTMLOpenTagComment *HOT =
+      new (Allocator) HTMLOpenTagComment(LocBegin, TagName);
+  return HOT;
 }
 
 HTMLOpenTagComment *Sema::actOnHTMLOpenTagFinish(
                               HTMLOpenTagComment *Tag,
                               ArrayRef<HTMLOpenTagComment::Attribute> Attrs,
-                              SourceLocation GreaterLoc) {
+                              SourceLocation GreaterLoc,
+                              bool IsSelfClosing) {
   Tag->setAttrs(Attrs);
   Tag->setGreaterLoc(GreaterLoc);
+  if (IsSelfClosing)
+    Tag->setSelfClosing();
+  else
+    HTMLOpenTags.push_back(Tag);
   return Tag;
 }
 
 HTMLCloseTagComment *Sema::actOnHTMLCloseTag(SourceLocation LocBegin,
                                              SourceLocation LocEnd,
                                              StringRef TagName) {
-  return new (Allocator) HTMLCloseTagComment(LocBegin, LocEnd, TagName);
+  HTMLCloseTagComment *HCT =
+      new (Allocator) HTMLCloseTagComment(LocBegin, LocEnd, TagName);
+  bool FoundOpen = false;
+  for (SmallVectorImpl<HTMLOpenTagComment *>::const_reverse_iterator
+       I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
+       I != E; ++I) {
+    if ((*I)->getTagName() == TagName) {
+      FoundOpen = true;
+      break;
+    }
+  }
+  if (!FoundOpen) {
+    Diag(HCT->getLocation(), diag::warn_doc_html_close_unbalanced)
+      << HCT->getSourceRange();
+    return HCT;
+  }
+
+  while (!HTMLOpenTags.empty()) {
+    const HTMLOpenTagComment *HOT = HTMLOpenTags.back();
+    HTMLOpenTags.pop_back();
+    StringRef LastNotClosedTagName = HOT->getTagName();
+    if (LastNotClosedTagName == TagName)
+      break;
+
+    if (!HTMLOpenTagNeedsClosing(LastNotClosedTagName))
+      continue;
+
+    bool OpenLineInvalid;
+    const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
+                                                HOT->getLocation(),
+                                                &OpenLineInvalid);
+    bool CloseLineInvalid;
+    const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
+                                                HCT->getLocation(),
+                                                &CloseLineInvalid);
+
+    if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
+      Diag(HOT->getLocation(), diag::warn_doc_html_open_close_mismatch)
+        << HOT->getTagName() << HCT->getTagName()
+        << HOT->getSourceRange() << HCT->getSourceRange();
+    else {
+      Diag(HOT->getLocation(), diag::warn_doc_html_open_close_mismatch)
+        << HOT->getTagName() << HCT->getTagName()
+        << HOT->getSourceRange();
+      Diag(HCT->getLocation(), diag::note_doc_html_closing_tag)
+        << HCT->getSourceRange();
+    }
+  }
+
+  return HCT;
 }
 
 FullComment *Sema::actOnFullComment(
@@ -219,6 +354,61 @@
   return new (Allocator) FullComment(Blocks);
 }
 
+void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
+  ParagraphComment *Paragraph = Command->getParagraph();
+  if (Paragraph->isWhitespace()) {
+    SourceLocation DiagLoc;
+    if (Command->getArgCount() > 0)
+      DiagLoc = Command->getArgRange(Command->getArgCount() - 1).getEnd();
+    if (!DiagLoc.isValid())
+      DiagLoc = Command->getCommandNameRange().getEnd();
+    Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
+      << Command->getCommandName()
+      << Command->getSourceRange();
+  }
+}
+
+unsigned Sema::resolveParmVarReference(StringRef Name,
+                                       const ParmVarDecl * const *ParamVars,
+                                       unsigned NumParams) {
+  for (unsigned i = 0; i != NumParams; ++i) {
+    const IdentifierInfo *II = ParamVars[i]->getIdentifier();
+    if (II && II->getName() == Name)
+      return i;
+  }
+  return ParamCommandComment::InvalidParamIndex;
+}
+
+unsigned Sema::correctTypoInParmVarReference(
+                                    StringRef Typo,
+                                    const ParmVarDecl * const *ParamVars,
+                                    unsigned NumParams) {
+  const unsigned MaxEditDistance = (Typo.size() + 2) / 3;
+  unsigned BestPVDIndex = NULL;
+  unsigned BestEditDistance = MaxEditDistance + 1;
+  for (unsigned i = 0; i != NumParams; ++i) {
+    const IdentifierInfo *II = ParamVars[i]->getIdentifier();
+    if (II) {
+      StringRef Name = II->getName();
+      unsigned MinPossibleEditDistance = abs(Name.size() - Typo.size());
+      if (MinPossibleEditDistance > 0 &&
+          Typo.size() / MinPossibleEditDistance < 3)
+        continue;
+
+      unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
+      if (EditDistance < BestEditDistance) {
+        BestEditDistance = EditDistance;
+        BestPVDIndex = i;
+      }
+    }
+  }
+
+  if (BestEditDistance <= MaxEditDistance)
+    return BestPVDIndex;
+  else
+    return ParamCommandComment::InvalidParamIndex;;
+}
+
 // TODO: tablegen
 bool Sema::isBlockCommand(StringRef Name) {
   return llvm::StringSwitch<bool>(Name)
@@ -259,7 +449,9 @@
 
 bool Sema::HTMLOpenTagNeedsClosing(StringRef Name) {
   return llvm::StringSwitch<bool>(Name)
-      .Case("br", true)
+      .Case("br", false)
+      .Case("hr", false)
+      .Case("li", false)
       .Default(true);
 }
 

Modified: cfe/trunk/lib/AST/RawCommentList.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/RawCommentList.cpp?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/lib/AST/RawCommentList.cpp (original)
+++ cfe/trunk/lib/AST/RawCommentList.cpp Wed Jul 11 16:38:39 2012
@@ -61,7 +61,7 @@
 RawComment::RawComment(const SourceManager &SourceMgr, SourceRange SR,
                        bool Merged) :
     Range(SR), RawTextValid(false), BriefTextValid(false),
-    IsAlmostTrailingComment(false),
+    IsAttached(false), IsAlmostTrailingComment(false),
     BeginLineValid(false), EndLineValid(false) {
   // Extract raw comment text, if possible.
   if (SR.getBegin() == SR.getEnd() || getRawText(SourceMgr).empty()) {

Modified: cfe/trunk/lib/Basic/DiagnosticIDs.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/DiagnosticIDs.cpp?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/lib/Basic/DiagnosticIDs.cpp (original)
+++ cfe/trunk/lib/Basic/DiagnosticIDs.cpp Wed Jul 11 16:38:39 2012
@@ -79,6 +79,7 @@
 #include "clang/Basic/DiagnosticLexKinds.inc"
 #include "clang/Basic/DiagnosticParseKinds.inc"
 #include "clang/Basic/DiagnosticASTKinds.inc"
+#include "clang/Basic/DiagnosticCommentKinds.inc"
 #include "clang/Basic/DiagnosticSemaKinds.inc"
 #include "clang/Basic/DiagnosticAnalysisKinds.inc"
 #undef DIAG

Modified: cfe/trunk/lib/Sema/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/CMakeLists.txt?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/CMakeLists.txt (original)
+++ cfe/trunk/lib/Sema/CMakeLists.txt Wed Jul 11 16:38:39 2012
@@ -46,6 +46,7 @@
   ClangARMNeon
   ClangAttrClasses
   ClangAttrList
+  ClangDiagnosticComment
   ClangDiagnosticSema
   ClangCommentNodes
   ClangDeclNodes

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Wed Jul 11 16:38:39 2012
@@ -21,6 +21,7 @@
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/CXXInheritance.h"
+#include "clang/AST/CommentDiagnostic.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
@@ -2703,6 +2704,8 @@
     }
   }
 
+  ActOnDocumentableDecl(TagD);
+
   return TagD;
 }
 
@@ -7104,9 +7107,55 @@
     }
   }
 
+  ActOnDocumentableDecls(Group, NumDecls);
+
   return DeclGroupPtrTy::make(DeclGroupRef::Create(Context, Group, NumDecls));
 }
 
+void Sema::ActOnDocumentableDecl(Decl *D) {
+  ActOnDocumentableDecls(&D, 1);
+}
+
+void Sema::ActOnDocumentableDecls(Decl **Group, unsigned NumDecls) {
+  // Don't parse the comment if Doxygen diagnostics are ignored.
+  if (NumDecls == 0 || !Group[0])
+   return;
+
+  if (Diags.getDiagnosticLevel(diag::warn_doc_param_not_found,
+                               Group[0]->getLocation())
+        == DiagnosticsEngine::Ignored)
+    return;
+
+  if (NumDecls >= 2) {
+    // This is a decl group.  Normally it will contain only declarations
+    // procuded from declarator list.  But in case we have any definitions or
+    // additional declaration references:
+    //   'typedef struct S {} S;'
+    //   'typedef struct S *S;'
+    //   'struct S *pS;'
+    // FinalizeDeclaratorGroup adds these as separate declarations.
+    Decl *MaybeTagDecl = Group[0];
+    if (MaybeTagDecl && isa<TagDecl>(MaybeTagDecl)) {
+      Group++;
+      NumDecls--;
+    }
+  }
+
+  // See if there are any new comments that are not attached to a decl.
+  ArrayRef<RawComment *> Comments = Context.getRawCommentList().getComments();
+  if (!Comments.empty() &&
+      !Comments.back()->isAttached()) {
+    // There is at least one comment that not attached to a decl.
+    // Maybe it should be attached to one of these decls?
+    //
+    // Note that this way we pick up not only comments that precede the
+    // declaration, but also comments that *follow* the declaration -- thanks to
+    // the lookahead in the lexer: we've consumed the semicolon and looked
+    // ahead through comments.
+    for (unsigned i = 0; i != NumDecls; ++i)
+      Context.getCommentForDecl(Group[i]);
+  }
+}
 
 /// ActOnParamDeclarator - Called from Parser::ParseFunctionDeclarator()
 /// to introduce parameters into function prototype scope.
@@ -8868,6 +8917,8 @@
   
   // Enter the tag context.
   PushDeclContext(S, Tag);
+
+  ActOnDocumentableDecl(TagD);
 }
 
 Decl *Sema::ActOnObjCContainerStartDefinition(Decl *IDecl) {
@@ -8877,6 +8928,7 @@
   assert(getContainingDC(OCD) == CurContext &&
       "The next DeclContext should be lexically contained in the current one.");
   CurContext = OCD;
+  ActOnDocumentableDecl(IDecl);
   return IDecl;
 }
 
@@ -10339,6 +10391,8 @@
     PushOnScopeChains(New, S);
   }
 
+  ActOnDocumentableDecl(New);
+
   return New;
 }
 

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Wed Jul 11 16:38:39 2012
@@ -5422,6 +5422,8 @@
     }
   }
 
+  ActOnDocumentableDecl(Namespc);
+
   // Although we could have an invalid decl (i.e. the namespace name is a
   // redefinition), push it as current DeclContext and try to continue parsing.
   // FIXME: We should be able to push Namespc here, so that the each DeclContext

Modified: cfe/trunk/lib/Sema/SemaDeclObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclObjC.cpp?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclObjC.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclObjC.cpp Wed Jul 11 16:38:39 2012
@@ -2952,7 +2952,9 @@
     if (InferRelatedResultType)
       ObjCMethod->SetRelatedResultType();
   }
-    
+
+  ActOnDocumentableDecl(ObjCMethod);
+
   return ObjCMethod;
 }
 

Removed: cfe/trunk/test/Sema/doxygen-comments.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/doxygen-comments.c?rev=160077&view=auto
==============================================================================
--- cfe/trunk/test/Sema/doxygen-comments.c (original)
+++ cfe/trunk/test/Sema/doxygen-comments.c (removed)
@@ -1,14 +0,0 @@
-// RUN: %clang_cc1 -fsyntax-only -Wdoxygen -verify %s
-// RUN: %clang_cc1 -fsyntax-only -Wdoxygen -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
-// RUN: cp %s %t
-// RUN: %clang_cc1 -fsyntax-only -Wdoxygen -fixit %t
-// RUN: %clang_cc1 -fsyntax-only -Wdoxygen -Werror %t
-
-struct a {
-  int x; //< comment // expected-warning {{not a Doxygen trailing comment}}
-  int y; /*< comment */ // expected-warning {{not a Doxygen trailing comment}}
-};
-
-// CHECK: fix-it:"{{.*}}":{8:10-8:13}:"///<"
-// CHECK: fix-it:"{{.*}}":{9:10-9:13}:"/**<"
-

Copied: cfe/trunk/test/Sema/warn-documentation-almost-trailing.c (from r160070, cfe/trunk/test/Sema/doxygen-comments.c)
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/warn-documentation-almost-trailing.c?p2=cfe/trunk/test/Sema/warn-documentation-almost-trailing.c&p1=cfe/trunk/test/Sema/doxygen-comments.c&r1=160070&r2=160078&rev=160078&view=diff
==============================================================================
--- cfe/trunk/test/Sema/doxygen-comments.c (original)
+++ cfe/trunk/test/Sema/warn-documentation-almost-trailing.c Wed Jul 11 16:38:39 2012
@@ -1,8 +1,8 @@
-// RUN: %clang_cc1 -fsyntax-only -Wdoxygen -verify %s
-// RUN: %clang_cc1 -fsyntax-only -Wdoxygen -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -fsyntax-only -Wdocumentation -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wdocumentation -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
 // RUN: cp %s %t
-// RUN: %clang_cc1 -fsyntax-only -Wdoxygen -fixit %t
-// RUN: %clang_cc1 -fsyntax-only -Wdoxygen -Werror %t
+// RUN: %clang_cc1 -fsyntax-only -Wdocumentation -fixit %t
+// RUN: %clang_cc1 -fsyntax-only -Wdocumentation -Werror %t
 
 struct a {
   int x; //< comment // expected-warning {{not a Doxygen trailing comment}}

Added: cfe/trunk/test/Sema/warn-documentation-fixits.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/warn-documentation-fixits.c?rev=160078&view=auto
==============================================================================
--- cfe/trunk/test/Sema/warn-documentation-fixits.c (added)
+++ cfe/trunk/test/Sema/warn-documentation-fixits.c Wed Jul 11 16:38:39 2012
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -fsyntax-only -Wdocumentation -verify %s
+// RUN: %clang_cc1 -fsyntax-only -Wdocumentation -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+/// \param ZZZZZZZZZZ Blah blah. expected-warning {{parameter 'ZZZZZZZZZZ' not found in the function declaration}}  expected-note {{did you mean 'a'?}}
+int test1(int a);
+
+/// \param aab Blah blah. expected-warning {{parameter 'aab' not found in the function declaration}}  expected-note {{did you mean 'aaa'?}}
+int test2(int aaa, int bbb);
+
+// CHECK: fix-it:"{{.*}}":{4:12-4:22}:"a"
+// CHECK: fix-it:"{{.*}}":{7:12-7:15}:"aaa"
+

Added: cfe/trunk/test/Sema/warn-documentation.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/warn-documentation.cpp?rev=160078&view=auto
==============================================================================
--- cfe/trunk/test/Sema/warn-documentation.cpp (added)
+++ cfe/trunk/test/Sema/warn-documentation.cpp Wed Jul 11 16:38:39 2012
@@ -0,0 +1,272 @@
+// RUN: %clang_cc1 -fsyntax-only -Wdocumentation -Wdocumentation-pedantic -verify %s
+
+// expected-warning at +1 {{expected quoted string after equals sign}}
+/// <a href=>
+int test_html1(int);
+
+// expected-warning at +1 {{expected quoted string after equals sign}}
+/// <a href==>
+int test_html2(int);
+
+// expected-warning at +2 {{expected quoted string after equals sign}}
+// expected-warning at +1 {{HTML opening tag prematurely ended, expected attribute name or '>'}}
+/// <a href= blah
+int test_html3(int);
+
+// expected-warning at +1 {{HTML opening tag prematurely ended, expected attribute name or '>'}}
+/// <a =>
+int test_html4(int);
+
+// expected-warning at +1 {{HTML opening tag prematurely ended, expected attribute name or '>'}}
+/// <a "aaa">
+int test_html5(int);
+
+// expected-warning at +1 {{HTML opening tag prematurely ended, expected attribute name or '>'}}
+/// <a a="b" =>
+int test_html6(int);
+
+// expected-warning at +1 {{HTML opening tag prematurely ended, expected attribute name or '>'}}
+/// <a a="b" "aaa">
+int test_html7(int);
+
+// expected-warning at +1 {{HTML opening tag prematurely ended, expected attribute name or '>'}}
+/// <a a="b" =
+int test_html8(int);
+
+// expected-warning at +2 {{HTML opening tag prematurely ended, expected attribute name or '>'}} expected-note at +1 {{HTML tag started here}}
+/** Aaa bbb<ccc ddd eee
+ * fff ggg.
+ */
+int test_html9(int);
+
+// expected-warning at +1 {{HTML opening tag prematurely ended, expected attribute name or '>'}}
+/** Aaa bbb<ccc ddd eee 42%
+ * fff ggg.
+ */
+int test_html10(int);
+
+
+/// <blockquote>Meow</blockquote>
+int test_html_nesting1(int);
+
+/// <b><i>Meow</i></b>
+int test_html_nesting2(int);
+
+/// <p>Aaa<br>
+/// Bbb</p>
+int test_html_nesting3(int);
+
+/// <p>Aaa<br />
+/// Bbb</p>
+int test_html_nesting4(int);
+
+// expected-warning at +1 {{HTML closing tag does not match any opening tag}}
+/// <b><i>Meow</a>
+int test_html_nesting5(int);
+
+// expected-warning at +2 {{HTML opening tag 'i' closed by 'b'}}
+// expected-warning at +1 {{HTML closing tag does not match any opening tag}}
+/// <b><i>Meow</b></b>
+int test_html_nesting6(int);
+
+// expected-warning at +2 {{HTML opening tag 'i' closed by 'b'}}
+// expected-warning at +1 {{HTML closing tag does not match any opening tag}}
+/// <b><i>Meow</b></i>
+int test_html_nesting7(int);
+
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+int test_block_command1(int);
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief \brief Aaa
+int test_block_command2(int);
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief
+/// \brief Aaa
+int test_block_command3(int);
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief
+///
+/// \brief Aaa
+int test_block_command4(int);
+
+// There is trailing whitespace on one of the following lines, don't remove it!
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief
+/// 
+/// \brief Aaa
+int test_block_command5(int);
+
+// expected-warning at +1 {{'\param' command used in a comment that is not attached to a function declaration}}
+/// \param a Blah blah.
+int test_param1;
+
+// expected-warning at +1 {{empty paragraph passed to '\param' command}}
+/// \param
+/// \param a Blah blah.
+int test_param2(int a);
+
+// expected-warning at +1 {{empty paragraph passed to '\param' command}}
+/// \param a
+int test_param3(int a);
+
+/// \param a Blah blah.
+int test_param4(int a);
+
+/// \param [in] a Blah blah.
+int test_param5(int a);
+
+/// \param [out] a Blah blah.
+int test_param6(int a);
+
+/// \param [in,out] a Blah blah.
+int test_param7(int a);
+
+// expected-warning at +1 {{whitespace is not allowed in parameter passing direction}}
+/// \param [ in ] a Blah blah.
+int test_param8(int a);
+
+// expected-warning at +1 {{whitespace is not allowed in parameter passing direction}}
+/// \param [in, out] a Blah blah.
+int test_param9(int a);
+
+// expected-warning at +1 {{unrecognized parameter passing direction, valid directions are '[in]', '[out]' and '[in,out]'}}
+/// \param [ junk] a Blah blah.
+int test_param10(int a);
+
+// expected-warning at +1 {{parameter 'A' not found in the function declaration}} expected-note at +1 {{did you mean 'a'?}}
+/// \param A Blah blah.
+int test_param11(int a);
+
+// expected-warning at +1 {{parameter 'aab' not found in the function declaration}} expected-note at +1 {{did you mean 'aaa'?}}
+/// \param aab Blah blah.
+int test_param12(int aaa, int bbb);
+
+// expected-warning at +1 {{parameter 'aab' not found in the function declaration}}
+/// \param aab Blah blah.
+int test_param13(int bbb, int ccc);
+
+class C {
+  // expected-warning at +1 {{parameter 'aaa' not found in the function declaration}}
+  /// \param aaa Blah blah.
+  C(int bbb, int ccc);
+
+  // expected-warning at +1 {{parameter 'aaa' not found in the function declaration}}
+  /// \param aaa Blah blah.
+ int test_param14(int bbb, int ccc);
+};
+
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+int test1; ///< \brief\brief Aaa
+
+// expected-warning at +2 {{empty paragraph passed to '\brief' command}}
+// expected-warning at +2 {{empty paragraph passed to '\brief' command}}
+int test2, ///< \brief\brief Aaa
+    test3; ///< \brief\brief Aaa
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+int test4; ///< \brief
+           ///< \brief Aaa
+
+
+// Check that we attach the comment to the declaration during parsing in the
+// following cases.  The test is based on the fact that we don't parse
+// documentation comments that are not attached to anything.
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+int test_attach1;
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+int test_attach2(int);
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+struct test_attach3 {
+  // expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+  /// \brief\brief Aaa
+  int test_attach4;
+
+  // expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+  int test_attach5; ///< \brief\brief Aaa
+
+  // expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+  /// \brief\brief Aaa
+  int test_attach6(int);
+};
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+class test_attach7 {
+  // expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+  /// \brief\brief Aaa
+  int test_attach8;
+
+  // expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+  int test_attach9; ///< \brief\brief Aaa
+
+  // expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+  /// \brief\brief Aaa
+  int test_attach10(int);
+};
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+enum test_attach9 {
+  // expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+  /// \brief\brief Aaa
+  test_attach10,
+
+  // expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+  test_attach11 ///< \brief\brief Aaa
+};
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+struct test_noattach12 *test_attach13;
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+typedef struct test_noattach14 *test_attach15;
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+typedef struct test_attach16 { int a; } test_attach17;
+
+struct S { int a; };
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+struct S *test_attach18;
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+typedef struct S *test_attach19;
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+struct test_attach20;
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+typedef struct test_attach21 {
+  // expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+  /// \brief\brief Aaa
+  int test_attach22;
+} test_attach23;
+
+// expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+/// \brief\brief Aaa
+namespace test_attach24 {
+  // expected-warning at +1 {{empty paragraph passed to '\brief' command}}
+  /// \brief\brief Aaa
+  namespace test_attach25 {
+  }
+}
+

Added: cfe/trunk/test/Sema/warn-documentation.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/warn-documentation.m?rev=160078&view=auto
==============================================================================
--- cfe/trunk/test/Sema/warn-documentation.m (added)
+++ cfe/trunk/test/Sema/warn-documentation.m Wed Jul 11 16:38:39 2012
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 -fsyntax-only -Wdocumentation -Wdocumentation-pedantic -verify %s
+
+ at class NSString;
+
+// expected-warning at +2 {{empty paragraph passed to '\brief' command}}
+/**
+ * \brief\brief Aaa
+ */
+ at interface A
+// expected-warning at +2 {{empty paragraph passed to '\brief' command}}
+/**
+ * \brief\brief Aaa
+ * \param aaa Aaa
+ * \param bbb Bbb
+ */
++ (NSString *)test1:(NSString *)aaa suffix:(NSString *)bbb;
+
+// expected-warning at +2 {{parameter 'aab' not found in the function declaration}} expected-note at +2 {{did you mean 'aaa'?}}
+/**
+ * \param aab Aaa
+ */
++ (NSString *)test2:(NSString *)aaa;
+ at end
+

Modified: cfe/trunk/tools/diagtool/DiagnosticNames.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/diagtool/DiagnosticNames.cpp?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/tools/diagtool/DiagnosticNames.cpp (original)
+++ cfe/trunk/tools/diagtool/DiagnosticNames.cpp Wed Jul 11 16:38:39 2012
@@ -39,6 +39,7 @@
 #include "clang/Basic/DiagnosticLexKinds.inc"
 #include "clang/Basic/DiagnosticParseKinds.inc"
 #include "clang/Basic/DiagnosticASTKinds.inc"
+#include "clang/Basic/DiagnosticCommentKinds.inc"
 #include "clang/Basic/DiagnosticSemaKinds.inc"
 #include "clang/Basic/DiagnosticAnalysisKinds.inc"
 #undef DIAG

Modified: cfe/trunk/unittests/AST/CommentLexer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/AST/CommentLexer.cpp?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/unittests/AST/CommentLexer.cpp (original)
+++ cfe/trunk/unittests/AST/CommentLexer.cpp Wed Jul 11 16:38:39 2012
@@ -1142,6 +1142,60 @@
 }
 
 TEST_F(CommentLexerTest, HTML15) {
+  const char *Sources[] = {
+    "// <tag/>",
+    "// <tag />"
+  };
+
+  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+    std::vector<Token> Toks;
+
+    lexString(Sources[i], Toks);
+
+    ASSERT_EQ(4U, Toks.size());
+
+    ASSERT_EQ(tok::text,               Toks[0].getKind());
+    ASSERT_EQ(StringRef(" "),          Toks[0].getText());
+
+    ASSERT_EQ(tok::html_tag_open,      Toks[1].getKind());
+    ASSERT_EQ(StringRef("tag"),        Toks[1].getHTMLTagOpenName());
+
+    ASSERT_EQ(tok::html_slash_greater, Toks[2].getKind());
+
+    ASSERT_EQ(tok::newline,            Toks[3].getKind());
+  }
+}
+
+TEST_F(CommentLexerTest, HTML16) {
+  const char *Sources[] = {
+    "// <tag/ Aaa",
+    "// <tag / Aaa"
+  };
+
+  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+    std::vector<Token> Toks;
+
+    lexString(Sources[i], Toks);
+
+    ASSERT_EQ(5U, Toks.size());
+
+    ASSERT_EQ(tok::text,               Toks[0].getKind());
+    ASSERT_EQ(StringRef(" "),          Toks[0].getText());
+
+    ASSERT_EQ(tok::html_tag_open,      Toks[1].getKind());
+    ASSERT_EQ(StringRef("tag"),        Toks[1].getHTMLTagOpenName());
+
+    ASSERT_EQ(tok::text,               Toks[2].getKind());
+    ASSERT_EQ(StringRef("/"),          Toks[2].getText());
+
+    ASSERT_EQ(tok::text,               Toks[3].getKind());
+    ASSERT_EQ(StringRef(" Aaa"),       Toks[3].getText());
+
+    ASSERT_EQ(tok::newline,            Toks[4].getKind());
+  }
+}
+
+TEST_F(CommentLexerTest, HTML17) {
   const char *Source = "// </";
 
   std::vector<Token> Toks;
@@ -1159,8 +1213,7 @@
   ASSERT_EQ(tok::newline,        Toks[2].getKind());
 }
 
-
-TEST_F(CommentLexerTest, HTML16) {
+TEST_F(CommentLexerTest, HTML18) {
   const char *Source = "// </@";
 
   std::vector<Token> Toks;
@@ -1181,7 +1234,7 @@
   ASSERT_EQ(tok::newline,        Toks[3].getKind());
 }
 
-TEST_F(CommentLexerTest, HTML17) {
+TEST_F(CommentLexerTest, HTML19) {
   const char *Source = "// </tag";
 
   std::vector<Token> Toks;
@@ -1199,7 +1252,7 @@
   ASSERT_EQ(tok::newline,        Toks[2].getKind());
 }
 
-TEST_F(CommentLexerTest, HTML18) {
+TEST_F(CommentLexerTest, HTML20) {
   const char *Sources[] = {
     "// </tag>",
     "// </ tag>",

Modified: cfe/trunk/unittests/AST/CommentParser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/AST/CommentParser.cpp?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/unittests/AST/CommentParser.cpp (original)
+++ cfe/trunk/unittests/AST/CommentParser.cpp Wed Jul 11 16:38:39 2012
@@ -57,8 +57,8 @@
   comments::Lexer L(Begin, CommentOptions(),
                     Source, Source + strlen(Source));
 
-  comments::Sema S(Allocator);
-  comments::Parser P(L, S, Allocator);
+  comments::Sema S(Allocator, SourceMgr, Diags);
+  comments::Parser P(L, S, Allocator, SourceMgr, Diags);
   comments::FullComment *FC = P.parseFullComment();
 
   if (DEBUG) {
@@ -292,6 +292,25 @@
   return ::testing::AssertionSuccess();
 }
 
+struct SelfClosing {};
+
+::testing::AssertionResult HasHTMLOpenTagAt(const Comment *C,
+                                            size_t Idx,
+                                            HTMLOpenTagComment *&HOT,
+                                            StringRef TagName,
+                                            SelfClosing) {
+  ::testing::AssertionResult AR = HasHTMLOpenTagAt(C, Idx, HOT, TagName);
+  if (!AR)
+    return AR;
+
+  if (!HOT->isSelfClosing())
+    return ::testing::AssertionFailure()
+        << "HTMLOpenTagComment is not self-closing";
+
+  return ::testing::AssertionSuccess();
+}
+
+
 struct NoAttrs {};
 
 ::testing::AssertionResult HasHTMLOpenTagAt(const Comment *C,
@@ -303,6 +322,10 @@
   if (!AR)
     return AR;
 
+  if (HOT->isSelfClosing())
+    return ::testing::AssertionFailure()
+        << "HTMLOpenTagComment is self-closing";
+
   if (HOT->getAttrCount() != 0)
     return ::testing::AssertionFailure()
         << "HTMLOpenTagComment has " << HOT->getAttrCount() << " attr(s), "
@@ -321,6 +344,10 @@
   if (!AR)
     return AR;
 
+  if (HOT->isSelfClosing())
+    return ::testing::AssertionFailure()
+        << "HTMLOpenTagComment is self-closing";
+
   if (HOT->getAttrCount() != 1)
     return ::testing::AssertionFailure()
         << "HTMLOpenTagComment has " << HOT->getAttrCount() << " attr(s), "
@@ -837,6 +864,28 @@
 
 TEST_F(CommentParserTest, HTML2) {
   const char *Sources[] = {
+    "// <br/>",
+    "// <br />"
+  };
+
+  for (size_t i = 0, e = array_lengthof(Sources); i != e; i++) {
+    FullComment *FC = parseString(Sources[i]);
+    ASSERT_TRUE(HasChildCount(FC, 1));
+
+    {
+      ParagraphComment *PC;
+      HTMLOpenTagComment *HOT;
+      ASSERT_TRUE(GetChildAt(FC, 0, PC));
+
+      ASSERT_TRUE(HasChildCount(PC, 2));
+        ASSERT_TRUE(HasTextAt(PC, 0, " "));
+        ASSERT_TRUE(HasHTMLOpenTagAt(PC, 1, HOT, "br", SelfClosing()));
+    }
+  }
+}
+
+TEST_F(CommentParserTest, HTML3) {
+  const char *Sources[] = {
     "// <a href",
     "// <a href ",
     "// <a href>",
@@ -859,7 +908,7 @@
   }
 }
 
-TEST_F(CommentParserTest, HTML3) {
+TEST_F(CommentParserTest, HTML4) {
   const char *Sources[] = {
     "// <a href=\"bbb\"",
     "// <a href=\"bbb\">",
@@ -881,7 +930,7 @@
   }
 }
 
-TEST_F(CommentParserTest, HTML4) {
+TEST_F(CommentParserTest, HTML5) {
   const char *Sources[] = {
     "// </a",
     "// </a>",
@@ -904,7 +953,7 @@
   }
 }
 
-TEST_F(CommentParserTest, HTML5) {
+TEST_F(CommentParserTest, HTML6) {
   const char *Source =
     "// <pre>\n"
     "// Aaa\n"

Modified: cfe/trunk/unittests/AST/Makefile
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/AST/Makefile?rev=160078&r1=160077&r2=160078&view=diff
==============================================================================
--- cfe/trunk/unittests/AST/Makefile (original)
+++ cfe/trunk/unittests/AST/Makefile Wed Jul 11 16:38:39 2012
@@ -10,6 +10,6 @@
 CLANG_LEVEL = ../..
 TESTNAME = AST
 LINK_COMPONENTS := support mc
-USEDLIBS = clangAST.a clangBasic.a
+USEDLIBS = clangAST.a clangLex.a clangBasic.a
 
 include $(CLANG_LEVEL)/unittests/Makefile





More information about the cfe-commits mailing list