Browse Source

Added full support for C++ documentation comments to the AST and parser.

pull/14/head
triton 12 years ago
parent
commit
a6fa8af39d
  1. 532
      src/AST/Comment.cs
  2. 3
      src/AST/Declaration.cs
  3. 280
      src/Parser/Comments.cpp
  4. 28
      src/Parser/Parser.cpp
  5. 3
      src/Parser/Parser.h
  6. 13
      src/Parser/Parser.lua

532
src/AST/Comment.cs

@ -1,10 +1,536 @@ @@ -1,10 +1,536 @@
namespace CppSharp.AST
using System.Collections.Generic;
namespace CppSharp.AST
{
/// <summary>
/// Represents a C++ comment.
/// Raw comment kind.
/// </summary>
public enum RawCommentKind
{
// Invalid comment.
Invalid,
// Any normal BCPL comments.
OrdinaryBCPL,
// Any normal C comment.
OrdinaryC,
// "/// stuff"
BCPLSlash,
// "//! stuff"
BCPLExcl,
// "/** stuff */"
JavaDoc,
// "/*! stuff */", also used by HeaderDoc
Qt,
// Two or more documentation comments merged together.
Merged
}
/// <summary>
/// Represents a raw C++ comment.
/// </summary>
public class RawComment
{
/// <summary>
/// Kind of the comment.
/// </summary>
public RawCommentKind Kind;
/// <summary>
/// Raw text of the comment.
/// </summary>
public string Text;
/// <summary>
/// Brief text if it is a documentation comment.
/// </summary>
public string BriefText;
/// <summary>
/// Returns if the comment is invalid.
/// </summary>
public bool IsInvalid
{
get { return Kind == RawCommentKind.Invalid; }
}
/// <summary>
/// Returns if the comment is ordinary (non-documentation).
/// </summary>
public bool IsOrdinary
{
get
{
return Kind == RawCommentKind.OrdinaryBCPL ||
Kind == RawCommentKind.OrdinaryC;
}
}
/// <summary>
/// Returns if this is a documentation comment.
/// </summary>
public bool IsDocumentation
{
get { return !IsInvalid && !IsOrdinary; }
}
/// <summary>
/// Provides the full comment information.
/// </summary>
public FullComment FullComment;
}
/// <summary>
/// Visitor for comments.
/// </summary>
public interface ICommentVisitor<out T>
{
T VisitBlockCommand(BlockCommandComment comment);
T VisitParamCommand(ParamCommandComment comment);
T VisitTParamCommand(TParamCommandComment comment);
T VisitVerbatimBlock(VerbatimBlockComment comment);
T VisitVerbatimLine(VerbatimLineComment comment);
T VisitParagraphCommand(ParagraphComment comment);
T VisitFull(FullComment comment);
T VisitHTMLStartTag(HTMLStartTagComment comment);
T VisitHTMLEndTag(HTMLEndTagComment comment);
T VisitText(TextComment comment);
T VisitInlineCommand(InlineCommandComment comment);
T VisitVerbatimBlockLine(VerbatimBlockLineComment comment);
}
/// <summary>
/// Any part of the comment.
/// </summary>
public abstract class Comment
{
protected Comment()
{
}
public abstract void Visit<T>(ICommentVisitor<T> visitor);
}
#region Comments
/// <summary>
/// A full comment attached to a declaration, contains block content.
/// </summary>
public class FullComment : Comment
{
public List<BlockContentComment> Blocks;
public FullComment()
{
Blocks = new List<BlockContentComment>();
}
public override void Visit<T>(ICommentVisitor<T> visitor)
{
visitor.VisitFull(this);
}
}
/// <summary>
/// Block content (contains inline content).
/// </summary>
public abstract class BlockContentComment : Comment
{
}
/// <summary>
/// A command that has zero or more word-like arguments (number of
/// word-like arguments depends on command name) and a paragraph as
/// an argument (e. g., \brief).
/// </summary>
public class BlockCommandComment : BlockContentComment
{
public struct Argument
{
public string Text;
public SourceLocation Location;
}
public uint CommandId;
public CommentCommandKind CommandKind
{
get { return (CommentCommandKind) CommandId; }
}
public List<Argument> Arguments;
public BlockCommandComment()
{
Arguments = new List<Argument>();
}
public override void Visit<T>(ICommentVisitor<T> visitor)
{
visitor.VisitBlockCommand(this);
}
}
/// <summary>
/// Doxygen \param command.
/// </summary>
public class ParamCommandComment : BlockCommandComment
{
public const uint InvalidParamIndex = ~0U;
public const uint VarArgParamIndex = ~0U/*InvalidParamIndex*/ - 1U;
public enum PassDirection
{
In,
Out,
InOut,
}
public bool IsParamIndexValid
{
get { return ParamIndex != InvalidParamIndex; }
}
public bool IsVarArgParam
{
get { return ParamIndex == VarArgParamIndex; }
}
public uint ParamIndex;
public PassDirection Direction;
public override void Visit<T>(ICommentVisitor<T> visitor)
{
visitor.VisitParamCommand(this);
}
}
/// <summary>
/// Doxygen \tparam command, describes a template parameter.
/// </summary>
public class TParamCommandComment : BlockCommandComment
{
/// If this template parameter name was resolved (found in template parameter
/// list), then this stores a list of position indexes in all template
/// parameter lists.
///
/// For example:
/// \verbatim
/// template<typename C, template<typename T> class TT>
/// void test(TT<int> aaa);
/// \endverbatim
/// For C: Position = { 0 }
/// For TT: Position = { 1 }
/// For T: Position = { 1, 0 }
public List<uint> Position;
public TParamCommandComment()
{
Position = new List<uint>();
}
public override void Visit<T>(ICommentVisitor<T> visitor)
{
visitor.VisitTParamCommand(this);
}
}
/// <summary>
/// A verbatim block command (e. g., preformatted code). Verbatim block
/// has an opening and a closing command and contains multiple lines of
/// text (VerbatimBlockLineComment nodes).
/// </summary>
public class Comment
public class VerbatimBlockComment : BlockCommandComment
{
public List<VerbatimBlockLineComment> Lines;
public VerbatimBlockComment()
{
Lines = new List<VerbatimBlockLineComment>();
}
public override void Visit<T>(ICommentVisitor<T> visitor)
{
visitor.VisitVerbatimBlock(this);
}
}
/// <summary>
/// A verbatim line command. Verbatim line has an opening command, a
/// single line of text (up to the newline after the opening command)
/// and has no closing command.
/// </summary>
public class VerbatimLineComment : BlockCommandComment
{
public string Text;
public override void Visit<T>(ICommentVisitor<T> visitor)
{
visitor.VisitVerbatimLine(this);
}
}
/// <summary>
/// A single paragraph that contains inline content.
/// </summary>
public class ParagraphComment : BlockContentComment
{
public List<InlineContentComment> Content;
public bool IsWhitespace;
public ParagraphComment()
{
Content = new List<InlineContentComment>();
}
public override void Visit<T>(ICommentVisitor<T> visitor)
{
visitor.VisitParagraphCommand(this);
}
}
/// <summary>
/// Inline content (contained within a block).
/// </summary>
public abstract class InlineContentComment : Comment
{
}
/// <summary>
/// Abstract class for opening and closing HTML tags. HTML tags are
/// always treated as inline content (regardless HTML semantics);
/// opening and closing tags are not matched.
/// </summary>
public abstract class HTMLTagComment : InlineContentComment
{
public string TagName;
}
/// <summary>
/// An opening HTML tag with attributes.
/// </summary>
public class HTMLStartTagComment : HTMLTagComment
{
public struct Attribute
{
public string Name;
public string Value;
}
public List<Attribute> Attributes;
public HTMLStartTagComment()
{
Attributes = new List<Attribute>();
}
public override void Visit<T>(ICommentVisitor<T> visitor)
{
visitor.VisitHTMLStartTag(this);
}
}
/// <summary>
/// A closing HTML tag.
/// </summary>
public class HTMLEndTagComment : HTMLTagComment
{
public override void Visit<T>(ICommentVisitor<T> visitor)
{
visitor.VisitHTMLEndTag(this);
}
}
/// <summary>
/// Plain text.
/// </summary>
public class TextComment : InlineContentComment
{
public string Text;
public override void Visit<T>(ICommentVisitor<T> visitor)
{
visitor.VisitText(this);
}
}
/// <summary>
/// A command with word-like arguments that is considered inline content.
/// </summary>
public class InlineCommandComment : Comment
{
public struct Argument
{
public string Text;
}
public enum RenderKind
{
RenderNormal,
RenderBold,
RenderMonospaced,
RenderEmphasized
}
public RenderKind Kind;
public List<Argument> Arguments;
public InlineCommandComment()
{
Arguments = new List<Argument>();
}
public override void Visit<T>(ICommentVisitor<T> visitor)
{
visitor.VisitInlineCommand(this);
}
}
/// <summary>
/// A line of text contained in a verbatim block.
/// </summary>
public class VerbatimBlockLineComment : BlockCommandComment
{
public string Text;
public override void Visit<T>(ICommentVisitor<T> visitor)
{
visitor.VisitVerbatimBlockLine(this);
}
}
#endregion
#region Commands
/// <summary>
/// Kinds of comment commands.
/// Synchronized from "clang/AST/CommentCommandList.inc".
/// </summary>
public enum CommentCommandKind
{
A,
Abstract,
Addtogroup,
Arg,
Attention,
Author,
Authors,
B,
Brief,
Bug,
C,
Callback,
Category,
Class,
Classdesign,
Coclass,
Code,
Endcode,
Const,
Constant,
Copyright,
Date,
Defgroup,
Dependency,
Deprecated,
Details,
Discussion,
Dot,
Enddot,
E,
Em,
Enum,
Flbrace,
Frbrace,
Flsquare,
Frsquare,
Fdollar,
Fn,
Function,
Functiongroup,
Headerfile,
Helper,
Helperclass,
Helps,
Htmlonly,
Endhtmlonly,
Ingroup,
Instancesize,
Interface,
Invariant,
Latexonly,
Endlatexonly,
Li,
Link,
Slashlink,
Mainpage,
Manonly,
Endmanonly,
Method,
Methodgroup,
Msc,
Endmsc,
Name,
Namespace,
Note,
Overload,
Ownership,
P,
Par,
Paragraph,
Param,
Performance,
Post,
Pre,
Property,
Protocol,
Ref,
Related,
Relatedalso,
Relates,
Relatesalso,
Remark,
Remarks,
Result,
Return,
Returns,
Rtfonly,
Endrtfonly,
Sa,
Section,
Security,
See,
Seealso,
Short,
Since,
Struct,
Subpage,
Subsection,
Subsubsection,
Superclass,
Template,
Templatefield,
Textblock,
Slashtextblock,
Todo,
Tparam,
Typedef,
Union,
Var,
Verbatim,
Endverbatim,
Version,
Warning,
Weakgroup,
Xmlonly,
Endxmlonly
}
#endregion
}

3
src/AST/Declaration.cs

@ -80,6 +80,9 @@ namespace CppSharp.AST @@ -80,6 +80,9 @@ namespace CppSharp.AST
}
}
// Comment associated with declaration.
public RawComment Comment;
// Doxygen-style brief comment.
public string BriefComment;

280
src/Parser/Comments.cpp

@ -0,0 +1,280 @@ @@ -0,0 +1,280 @@
/************************************************************************
*
* CppSharp
* Licensed under the simplified BSD license. All rights reserved.
*
************************************************************************/
#include "Parser.h"
#include "Interop.h"
#include <clang/AST/ASTContext.h>
//-----------------------------------//
static CppSharp::AST::RawCommentKind
ConvertCommentKind(clang::RawComment::CommentKind Kind)
{
using clang::RawComment;
using namespace CppSharp::AST;
switch(Kind)
{
case RawComment::RCK_Invalid: return RawCommentKind::Invalid;
case RawComment::RCK_OrdinaryBCPL: return RawCommentKind::OrdinaryBCPL;
case RawComment::RCK_OrdinaryC: return RawCommentKind::OrdinaryC;
case RawComment::RCK_BCPLSlash: return RawCommentKind::BCPLSlash;
case RawComment::RCK_BCPLExcl: return RawCommentKind::BCPLExcl;
case RawComment::RCK_JavaDoc: return RawCommentKind::JavaDoc;
case RawComment::RCK_Qt: return RawCommentKind::Qt;
case RawComment::RCK_Merged: return RawCommentKind::Merged;
}
llvm_unreachable("Unknown comment kind");
}
CppSharp::AST::RawComment^ Parser::WalkRawComment(const clang::RawComment* RC)
{
using namespace clang;
using namespace clix;
auto &SM = C->getSourceManager();
auto Comment = gcnew CppSharp::AST::RawComment();
Comment->Kind = ConvertCommentKind(RC->getKind());
Comment->Text = marshalString<E_UTF8>(RC->getRawText(SM));
Comment->BriefText = marshalString<E_UTF8>(RC->getBriefText(*AST));
return Comment;
}
static CppSharp::AST::InlineCommandComment::RenderKind
ConvertRenderKind(clang::comments::InlineCommandComment::RenderKind Kind)
{
using namespace clang::comments;
switch(Kind)
{
case InlineCommandComment::RenderNormal:
return CppSharp::AST::InlineCommandComment::RenderKind::RenderNormal;
case InlineCommandComment::RenderBold:
return CppSharp::AST::InlineCommandComment::RenderKind::RenderBold;
case InlineCommandComment::RenderMonospaced:
return CppSharp::AST::InlineCommandComment::RenderKind::RenderMonospaced;
case InlineCommandComment::RenderEmphasized:
return CppSharp::AST::InlineCommandComment::RenderKind::RenderEmphasized;
}
llvm_unreachable("Unknown render kind");
}
static CppSharp::AST::ParamCommandComment::PassDirection
ConvertParamPassDirection(clang::comments::ParamCommandComment::PassDirection Dir)
{
using namespace clang::comments;
switch(Dir)
{
case ParamCommandComment::In:
return CppSharp::AST::ParamCommandComment::PassDirection::In;
case ParamCommandComment::Out:
return CppSharp::AST::ParamCommandComment::PassDirection::Out;
case ParamCommandComment::InOut:
return CppSharp::AST::ParamCommandComment::PassDirection::InOut;
}
llvm_unreachable("Unknown parameter pass direction");
}
static void HandleBlockCommand(const clang::comments::BlockCommandComment *CK,
CppSharp::AST::BlockCommandComment^ BC)
{
using namespace clix;
BC->CommandId = CK->getCommandID();
for (unsigned I = 0, E = CK->getNumArgs(); I != E; ++E)
{
auto Arg = CppSharp::AST::BlockCommandComment::Argument();
Arg.Text = marshalString<E_UTF8>(CK->getArgText(I));
BC->Arguments->Add(Arg);
}
}
static CppSharp::AST::Comment^ ConvertCommentBlock(clang::comments::Comment* C)
{
using namespace clang;
using clang::comments::Comment;
using namespace clix;
using namespace CppSharp::AST;
// This needs to have an underscore else we get an ICE under VS2012.
CppSharp::AST::Comment^ _Comment;
switch(C->getCommentKind())
{
case Comment::BlockCommandCommentKind:
{
auto CK = cast<const clang::comments::BlockCommandComment>(C);
auto BC = gcnew BlockCommandComment();
_Comment = BC;
HandleBlockCommand(CK, BC);
break;
}
case Comment::ParamCommandCommentKind:
{
auto CK = cast<clang::comments::ParamCommandComment>(C);
auto PC = gcnew ParamCommandComment();
_Comment = PC;
HandleBlockCommand(CK, PC);
PC->Direction = ConvertParamPassDirection(CK->getDirection());
PC->ParamIndex = CK->getParamIndex();
break;
}
case Comment::TParamCommandCommentKind:
{
auto CK = cast<clang::comments::TParamCommandComment>(C);
_Comment = gcnew TParamCommandComment();
auto TC = gcnew TParamCommandComment();
_Comment = TC;
HandleBlockCommand(CK, TC);
if (CK->isPositionValid())
for (unsigned I = 0, E = CK->getDepth(); I != E; ++I)
TC->Position->Add(CK->getIndex(I));
break;
}
case Comment::VerbatimBlockCommentKind:
{
auto CK = cast<clang::comments::VerbatimBlockComment>(C);
auto VB = gcnew VerbatimBlockComment();
_Comment = VB;
for (auto I = CK->child_begin(), E = CK->child_end(); I != E; ++I)
{
auto Line = ConvertCommentBlock(*I);
VB->Lines->Add(safe_cast<VerbatimBlockLineComment^>(Line));
}
break;
}
case Comment::VerbatimLineCommentKind:
{
auto CK = cast<clang::comments::VerbatimLineComment>(C);
auto VL = gcnew VerbatimLineComment();
_Comment = VL;
VL->Text = marshalString<E_UTF8>(CK->getText());
break;
}
case Comment::ParagraphCommentKind:
{
auto CK = cast<clang::comments::ParagraphComment>(C);
auto PC = gcnew ParagraphComment();
_Comment = PC;
for (auto I = CK->child_begin(), E = CK->child_end(); I != E; ++I)
{
auto Content = ConvertCommentBlock(*I);
PC->Content->Add(safe_cast<InlineContentComment^>(Content));
}
PC->IsWhitespace = CK->isWhitespace();
break;
}
case Comment::FullCommentKind:
{
auto CK = cast<clang::comments::FullComment>(C);
auto FC = gcnew FullComment();
_Comment = FC;
for (auto I = CK->child_begin(), E = CK->child_end(); I != E; ++I)
{
auto Content = ConvertCommentBlock(*I);
FC->Blocks->Add(safe_cast<BlockContentComment^>(Content));
}
break;
}
case Comment::HTMLStartTagCommentKind:
{
auto CK = cast<clang::comments::HTMLStartTagComment>(C);
auto TC = gcnew HTMLStartTagComment();
_Comment = TC;
TC->TagName = marshalString<E_UTF8>(CK->getTagName());
for (unsigned I = 0, E = CK->getNumAttrs(); I != E; ++E)
{
auto A = CK->getAttr(I);
auto Attr = CppSharp::AST::HTMLStartTagComment::Attribute();
Attr.Name = marshalString<E_UTF8>(A.Name);
Attr.Value = marshalString<E_UTF8>(A.Value);
TC->Attributes->Add(Attr);
}
break;
}
case Comment::HTMLEndTagCommentKind:
{
auto CK = cast<clang::comments::HTMLEndTagComment>(C);
auto TC = gcnew HTMLEndTagComment();
_Comment = TC;
TC->TagName = marshalString<E_UTF8>(CK->getTagName());
break;
}
case Comment::TextCommentKind:
{
auto CK = cast<clang::comments::TextComment>(C);
auto TC = gcnew TextComment();
_Comment = TC;
TC->Text = marshalString<E_UTF8>(CK->getText());
break;
}
case Comment::InlineCommandCommentKind:
{
auto CK = cast<clang::comments::InlineCommandComment>(C);
auto IC = gcnew InlineCommandComment();
_Comment = IC;
IC->Kind = ConvertRenderKind(CK->getRenderKind());
for (unsigned I = 0, E = CK->getNumArgs(); I != E; ++E)
{
auto Arg = CppSharp::AST::InlineCommandComment::Argument();
Arg.Text = marshalString<E_UTF8>(CK->getArgText(I));
IC->Arguments->Add(Arg);
}
break;
}
case Comment::VerbatimBlockLineCommentKind:
{
auto CK = cast<clang::comments::VerbatimBlockLineComment>(C);
auto VL = gcnew VerbatimBlockLineComment();
_Comment = VL;
VL->Text = marshalString<E_UTF8>(CK->getText());
break;
}
case Comment::NoCommentKind: return nullptr;
default:
llvm_unreachable("Unknown comment kind");
}
assert(_Comment && "Invalid comment instance");
return _Comment;
}
void Parser::HandleComments(clang::Decl* D, CppSharp::AST::Declaration^ Decl)
{
using namespace clang;
using namespace clang::comments;
using namespace clix;
const RawComment* RC = 0;
if (!(RC = AST->getRawCommentForAnyRedecl(D)))
return;
auto RawComment = WalkRawComment(RC);
Decl->Comment = RawComment;
if (FullComment* FC = RC->parse(*AST, &C->getPreprocessor(), D))
{
auto CB = safe_cast<CppSharp::AST::FullComment^>(ConvertCommentBlock(FC));
RawComment->FullComment = CB;
}
// Debug Text
SourceManager& SM = C->getSourceManager();
const LangOptions& LangOpts = C->getLangOpts();
auto Range = CharSourceRange::getTokenRange(D->getSourceRange());
bool Invalid;
StringRef DeclText = Lexer::getSourceText(Range, SM, LangOpts, &Invalid);
//assert(!Invalid && "Should have a valid location");
if (!Invalid)
Decl->DebugText = marshalString<E_UTF8>(DeclText);
}

28
src/Parser/Parser.cpp

@ -14,6 +14,7 @@ @@ -14,6 +14,7 @@
#include <clang/Basic/Version.h>
#include <clang/Config/config.h>
#include <clang/AST/ASTContext.h>
#include <clang/AST/Comment.h>
#include <clang/AST/DeclTemplate.h>
#include <clang/Lex/DirectoryLookup.h>
#include <clang/Lex/HeaderSearch.h>
@ -1529,33 +1530,6 @@ CppSharp::AST::Variable^ Parser::WalkVariable(clang::VarDecl *VD) @@ -1529,33 +1530,6 @@ CppSharp::AST::Variable^ Parser::WalkVariable(clang::VarDecl *VD)
//-----------------------------------//
void Parser::HandleComments(clang::Decl* D, CppSharp::AST::Declaration^ Decl)
{
using namespace clang;
using namespace clix;
// Get the declaration comment.
std::string BriefText;
if (const RawComment* Comment = AST->getRawCommentForAnyRedecl(D))
BriefText = Comment->getBriefText(*AST);
Decl->BriefComment = marshalString<E_UTF8>(BriefText);
SourceManager& SM = C->getSourceManager();
const LangOptions& LangOpts = C->getLangOpts();
auto Range = CharSourceRange::getTokenRange(D->getSourceRange());
bool Invalid;
StringRef DeclText = Lexer::getSourceText(Range, SM, LangOpts, &Invalid);
//assert(!Invalid && "Should have a valid location");
if (!Invalid)
Decl->DebugText = marshalString<E_UTF8>(DeclText);
}
//-----------------------------------//
bool Parser::GetDeclText(clang::SourceRange SR, std::string& Text)
{
using namespace clang;

3
src/Parser/Parser.h

@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
#include <clang/Basic/IdentifierTable.h>
#include <clang/AST/ASTConsumer.h>
#include <clang/AST/Mangle.h>
#include <clang/AST/RawCommentList.h>
#include <clang/AST/Comment.h>
#include <clang/AST/RecordLayout.h>
#include <clang/Lex/Preprocessor.h>
#include <clang/Lex/PreprocessingRecord.h>
@ -144,6 +146,7 @@ protected: @@ -144,6 +146,7 @@ protected:
CppSharp::AST::FunctionTemplate^ Parser::WalkFunctionTemplate(
clang::FunctionTemplateDecl*);
CppSharp::AST::Variable^ WalkVariable(clang::VarDecl*);
CppSharp::AST::RawComment^ WalkRawComment(const clang::RawComment*);
CppSharp::AST::Type^ WalkType(clang::QualType, clang::TypeLoc* = 0,
bool DesugarType = false);

13
src/Parser/Parser.lua

@ -17,14 +17,18 @@ project "CppSharp.Parser" @@ -17,14 +17,18 @@ project "CppSharp.Parser"
-- premake build. remove this once this support is added
-- at the project level.
configuration { "*Main.cpp" }
configuration { "Main.cpp" }
flags { "Managed" }
usingdirs { libdir }
configuration { "*Parser.cpp" }
configuration { "Parser.cpp" }
flags { "Managed" }
usingdirs { libdir }
configuration { "Comments.cpp" }
flags { "Managed" }
usingdirs { libdir }
configuration "vs*"
buildoptions { clang_msvc_flags }
files { "VSLookup.cpp" }
@ -34,8 +38,7 @@ project "CppSharp.Parser" @@ -34,8 +38,7 @@ project "CppSharp.Parser"
files
{
"**.h",
"Main.cpp",
"Parser.cpp",
"*.cpp",
"**.lua"
}

Loading…
Cancel
Save