diff --git a/src/AST/Comment.cs b/src/AST/Comment.cs index 5e26aeff..546f6df4 100644 --- a/src/AST/Comment.cs +++ b/src/AST/Comment.cs @@ -1,10 +1,536 @@ -namespace CppSharp.AST +using System.Collections.Generic; + +namespace CppSharp.AST { /// - /// Represents a C++ comment. + /// Raw comment kind. + /// + 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 + } + + /// + /// Represents a raw C++ comment. + /// + public class RawComment + { + /// + /// Kind of the comment. + /// + public RawCommentKind Kind; + + /// + /// Raw text of the comment. + /// + public string Text; + + /// + /// Brief text if it is a documentation comment. + /// + public string BriefText; + + /// + /// Returns if the comment is invalid. + /// + public bool IsInvalid + { + get { return Kind == RawCommentKind.Invalid; } + } + + /// + /// Returns if the comment is ordinary (non-documentation). + /// + public bool IsOrdinary + { + get + { + return Kind == RawCommentKind.OrdinaryBCPL || + Kind == RawCommentKind.OrdinaryC; + } + } + + /// + /// Returns if this is a documentation comment. + /// + public bool IsDocumentation + { + get { return !IsInvalid && !IsOrdinary; } + } + + /// + /// Provides the full comment information. + /// + public FullComment FullComment; + } + + /// + /// Visitor for comments. + /// + public interface ICommentVisitor + { + 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); + } + + /// + /// Any part of the comment. + /// + public abstract class Comment + { + protected Comment() + { + } + + public abstract void Visit(ICommentVisitor visitor); + } + + #region Comments + + /// + /// A full comment attached to a declaration, contains block content. + /// + public class FullComment : Comment + { + public List Blocks; + + public FullComment() + { + Blocks = new List(); + } + + public override void Visit(ICommentVisitor visitor) + { + visitor.VisitFull(this); + } + } + + /// + /// Block content (contains inline content). + /// + public abstract class BlockContentComment : Comment + { + + } + + /// + /// 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). + /// + 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 Arguments; + + public BlockCommandComment() + { + Arguments = new List(); + } + + public override void Visit(ICommentVisitor visitor) + { + visitor.VisitBlockCommand(this); + } + } + + /// + /// Doxygen \param command. + /// + 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(ICommentVisitor visitor) + { + visitor.VisitParamCommand(this); + } + } + + /// + /// Doxygen \tparam command, describes a template parameter. + /// + 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 class TT> + /// void test(TT aaa); + /// \endverbatim + /// For C: Position = { 0 } + /// For TT: Position = { 1 } + /// For T: Position = { 1, 0 } + public List Position; + + public TParamCommandComment() + { + Position = new List(); + } + + public override void Visit(ICommentVisitor visitor) + { + visitor.VisitTParamCommand(this); + } + } + + /// + /// 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). /// - public class Comment + public class VerbatimBlockComment : BlockCommandComment { + public List Lines; + public VerbatimBlockComment() + { + Lines = new List(); + } + + public override void Visit(ICommentVisitor visitor) + { + visitor.VisitVerbatimBlock(this); + } + } + + /// + /// 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. + /// + public class VerbatimLineComment : BlockCommandComment + { + public string Text; + + public override void Visit(ICommentVisitor visitor) + { + visitor.VisitVerbatimLine(this); + } + } + + /// + /// A single paragraph that contains inline content. + /// + public class ParagraphComment : BlockContentComment + { + public List Content; + + public bool IsWhitespace; + + public ParagraphComment() + { + Content = new List(); + } + + public override void Visit(ICommentVisitor visitor) + { + visitor.VisitParagraphCommand(this); + } } + + /// + /// Inline content (contained within a block). + /// + public abstract class InlineContentComment : Comment + { + + } + + /// + /// 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. + /// + public abstract class HTMLTagComment : InlineContentComment + { + public string TagName; + } + + /// + /// An opening HTML tag with attributes. + /// + public class HTMLStartTagComment : HTMLTagComment + { + public struct Attribute + { + public string Name; + public string Value; + } + + public List Attributes; + + public HTMLStartTagComment() + { + Attributes = new List(); + } + + public override void Visit(ICommentVisitor visitor) + { + visitor.VisitHTMLStartTag(this); + } + } + + /// + /// A closing HTML tag. + /// + public class HTMLEndTagComment : HTMLTagComment + { + public override void Visit(ICommentVisitor visitor) + { + visitor.VisitHTMLEndTag(this); + } + } + + /// + /// Plain text. + /// + public class TextComment : InlineContentComment + { + public string Text; + + public override void Visit(ICommentVisitor visitor) + { + visitor.VisitText(this); + } + } + + /// + /// A command with word-like arguments that is considered inline content. + /// + public class InlineCommandComment : Comment + { + public struct Argument + { + public string Text; + } + + public enum RenderKind + { + RenderNormal, + RenderBold, + RenderMonospaced, + RenderEmphasized + } + + public RenderKind Kind; + + public List Arguments; + + public InlineCommandComment() + { + Arguments = new List(); + } + + public override void Visit(ICommentVisitor visitor) + { + visitor.VisitInlineCommand(this); + } + } + + /// + /// A line of text contained in a verbatim block. + /// + public class VerbatimBlockLineComment : BlockCommandComment + { + public string Text; + + public override void Visit(ICommentVisitor visitor) + { + visitor.VisitVerbatimBlockLine(this); + } + } + + #endregion + + #region Commands + + /// + /// Kinds of comment commands. + /// Synchronized from "clang/AST/CommentCommandList.inc". + /// + 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 + } diff --git a/src/AST/Declaration.cs b/src/AST/Declaration.cs index c715d347..a9c9fa67 100644 --- a/src/AST/Declaration.cs +++ b/src/AST/Declaration.cs @@ -80,6 +80,9 @@ namespace CppSharp.AST } } + // Comment associated with declaration. + public RawComment Comment; + // Doxygen-style brief comment. public string BriefComment; diff --git a/src/Parser/Comments.cpp b/src/Parser/Comments.cpp new file mode 100644 index 00000000..d80d478f --- /dev/null +++ b/src/Parser/Comments.cpp @@ -0,0 +1,280 @@ +/************************************************************************ +* +* CppSharp +* Licensed under the simplified BSD license. All rights reserved. +* +************************************************************************/ + +#include "Parser.h" +#include "Interop.h" + +#include + +//-----------------------------------// + +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(RC->getRawText(SM)); + Comment->BriefText = marshalString(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(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(C); + auto BC = gcnew BlockCommandComment(); + _Comment = BC; + HandleBlockCommand(CK, BC); + break; + } + case Comment::ParamCommandCommentKind: + { + auto CK = cast(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(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(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(Line)); + } + break; + } + case Comment::VerbatimLineCommentKind: + { + auto CK = cast(C); + auto VL = gcnew VerbatimLineComment(); + _Comment = VL; + VL->Text = marshalString(CK->getText()); + break; + } + case Comment::ParagraphCommentKind: + { + auto CK = cast(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(Content)); + } + PC->IsWhitespace = CK->isWhitespace(); + break; + } + case Comment::FullCommentKind: + { + auto CK = cast(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(Content)); + } + break; + } + case Comment::HTMLStartTagCommentKind: + { + auto CK = cast(C); + auto TC = gcnew HTMLStartTagComment(); + _Comment = TC; + TC->TagName = marshalString(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(A.Name); + Attr.Value = marshalString(A.Value); + TC->Attributes->Add(Attr); + } + break; + } + case Comment::HTMLEndTagCommentKind: + { + auto CK = cast(C); + auto TC = gcnew HTMLEndTagComment(); + _Comment = TC; + TC->TagName = marshalString(CK->getTagName()); + break; + } + case Comment::TextCommentKind: + { + auto CK = cast(C); + auto TC = gcnew TextComment(); + _Comment = TC; + TC->Text = marshalString(CK->getText()); + break; + } + case Comment::InlineCommandCommentKind: + { + auto CK = cast(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(CK->getArgText(I)); + IC->Arguments->Add(Arg); + } + break; + } + case Comment::VerbatimBlockLineCommentKind: + { + auto CK = cast(C); + auto VL = gcnew VerbatimBlockLineComment(); + _Comment = VL; + VL->Text = marshalString(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(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(DeclText); +} diff --git a/src/Parser/Parser.cpp b/src/Parser/Parser.cpp index bd57bd2a..7584be79 100644 --- a/src/Parser/Parser.cpp +++ b/src/Parser/Parser.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -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(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(DeclText); -} - -//-----------------------------------// - bool Parser::GetDeclText(clang::SourceRange SR, std::string& Text) { using namespace clang; diff --git a/src/Parser/Parser.h b/src/Parser/Parser.h index b88b25d0..d91065ae 100644 --- a/src/Parser/Parser.h +++ b/src/Parser/Parser.h @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include #include @@ -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); diff --git a/src/Parser/Parser.lua b/src/Parser/Parser.lua index 2448b24f..8e76aa93 100644 --- a/src/Parser/Parser.lua +++ b/src/Parser/Parser.lua @@ -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" files { "**.h", - "Main.cpp", - "Parser.cpp", + "*.cpp", "**.lua" }