diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/AST/ASTContext.cpp | 11 | ||||
-rw-r--r-- | lib/AST/CommentBriefParser.cpp | 1 | ||||
-rw-r--r-- | lib/AST/CommentDumper.cpp | 19 | ||||
-rw-r--r-- | lib/AST/CommentParser.cpp | 42 | ||||
-rw-r--r-- | lib/AST/CommentSema.cpp | 281 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 1 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 6 |
7 files changed, 327 insertions, 34 deletions
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 46a4d87f9a..edcfe8ea7a 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -72,6 +72,13 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const { if (isa<ParmVarDecl>(D)) return NULL; + // TODO: we could look up template parameter documentation in the template + // documentation. + if (isa<TemplateTypeParmDecl>(D) || + isa<NonTypeTemplateParmDecl>(D) || + isa<TemplateTemplateParmDecl>(D)) + return NULL; + ArrayRef<RawComment *> RawComments = Comments.getComments(); // If there are no comments anywhere, we won't find anything. @@ -86,7 +93,9 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const { // so we use the location of the identifier as the "declaration location". SourceLocation DeclLoc; if (isa<ObjCMethodDecl>(D) || isa<ObjCContainerDecl>(D) || - isa<ObjCPropertyDecl>(D)) + isa<ObjCPropertyDecl>(D) || + isa<FunctionTemplateDecl>(D) || + isa<ClassTemplateDecl>(D) || isa<ClassTemplateSpecializationDecl>(D)) DeclLoc = D->getLocStart(); else DeclLoc = D->getLocation(); diff --git a/lib/AST/CommentBriefParser.cpp b/lib/AST/CommentBriefParser.cpp index 47d52e19a2..22209c097f 100644 --- a/lib/AST/CommentBriefParser.cpp +++ b/lib/AST/CommentBriefParser.cpp @@ -48,6 +48,7 @@ bool isBlockCommand(StringRef Name) { .Case("pre", true) .Case("post", true) .Cases("param", "arg", true) + .Case("tparam", true) .Default(false); } } // unnamed namespace diff --git a/lib/AST/CommentDumper.cpp b/lib/AST/CommentDumper.cpp index f02ea334cd..dffc8233a0 100644 --- a/lib/AST/CommentDumper.cpp +++ b/lib/AST/CommentDumper.cpp @@ -50,6 +50,7 @@ public: void visitParagraphComment(const ParagraphComment *C); void visitBlockCommandComment(const BlockCommandComment *C); void visitParamCommandComment(const ParamCommandComment *C); + void visitTParamCommandComment(const TParamCommandComment *C); void visitVerbatimBlockComment(const VerbatimBlockComment *C); void visitVerbatimBlockLineComment(const VerbatimBlockLineComment *C); void visitVerbatimLineComment(const VerbatimLineComment *C); @@ -176,6 +177,24 @@ void CommentDumper::visitParamCommandComment(const ParamCommandComment *C) { OS << " ParamIndex=" << C->getParamIndex(); } +void CommentDumper::visitTParamCommandComment(const TParamCommandComment *C) { + dumpComment(C); + + if (C->hasParamName()) { + OS << " Param=\"" << C->getParamName() << "\""; + } + + if (C->isPositionValid()) { + OS << " Position=<"; + for (unsigned i = 0, e = C->getDepth(); i != e; ++i) { + OS << C->getIndex(i); + if (i != e - 1) + OS << ", "; + } + OS << ">"; + } +} + void CommentDumper::visitVerbatimBlockComment(const VerbatimBlockComment *C) { dumpComment(C); diff --git a/lib/AST/CommentParser.cpp b/lib/AST/CommentParser.cpp index 6d535673f8..63560e197d 100644 --- a/lib/AST/CommentParser.cpp +++ b/lib/AST/CommentParser.cpp @@ -276,6 +276,19 @@ ParamCommandComment *Parser::parseParamCommandArgs( return PC; } +TParamCommandComment *Parser::parseTParamCommandArgs( + TParamCommandComment *TPC, + TextTokenRetokenizer &Retokenizer) { + Token Arg; + if (Retokenizer.lexWord(Arg)) + TPC = S.actOnTParamCommandParamNameArg(TPC, + Arg.getLocation(), + Arg.getEndLocation(), + Arg.getText()); + + return TPC; +} + BlockCommandComment *Parser::parseBlockCommandArgs( BlockCommandComment *BC, TextTokenRetokenizer &Retokenizer, @@ -299,14 +312,21 @@ BlockCommandComment *Parser::parseBlockCommand() { assert(Tok.is(tok::command)); ParamCommandComment *PC; + TParamCommandComment *TPC; BlockCommandComment *BC; bool IsParam = false; + bool IsTParam = false; unsigned NumArgs = 0; if (S.isParamCommand(Tok.getCommandName())) { IsParam = true; PC = S.actOnParamCommandStart(Tok.getLocation(), Tok.getEndLocation(), Tok.getCommandName()); + } if (S.isTParamCommand(Tok.getCommandName())) { + IsTParam = true; + TPC = S.actOnTParamCommandStart(Tok.getLocation(), + Tok.getEndLocation(), + Tok.getCommandName()); } else { NumArgs = S.getBlockCommandNumArgs(Tok.getCommandName()); BC = S.actOnBlockCommandStart(Tok.getLocation(), @@ -323,13 +343,15 @@ BlockCommandComment *Parser::parseBlockCommand() { return S.actOnBlockCommandFinish(IsParam ? PC : BC, Paragraph); } - if (IsParam || NumArgs > 0) { + if (IsParam || IsTParam || NumArgs > 0) { // In order to parse command arguments we need to retokenize a few // following text tokens. TextTokenRetokenizer Retokenizer(Allocator, *this); if (IsParam) PC = parseParamCommandArgs(PC, Retokenizer); + else if (IsTParam) + TPC = parseTParamCommandArgs(TPC, Retokenizer); else BC = parseBlockCommandArgs(BC, Retokenizer, NumArgs); @@ -341,6 +363,8 @@ BlockCommandComment *Parser::parseBlockCommand() { // paragraph. if (IsParam) return S.actOnParamCommandFinish(PC, cast<ParagraphComment>(Block)); + else if (IsTParam) + return S.actOnTParamCommandFinish(TPC, cast<ParagraphComment>(Block)); else return S.actOnBlockCommandFinish(BC, cast<ParagraphComment>(Block)); } @@ -419,7 +443,7 @@ HTMLStartTagComment *Parser::parseHTMLStartTag() { case tok::html_greater: HST = S.actOnHTMLStartTagFinish(HST, - copyArray(llvm::makeArrayRef(Attrs)), + S.copyArray(llvm::makeArrayRef(Attrs)), Tok.getLocation(), /* IsSelfClosing = */ false); consumeToken(); @@ -427,7 +451,7 @@ HTMLStartTagComment *Parser::parseHTMLStartTag() { case tok::html_slash_greater: HST = S.actOnHTMLStartTagFinish(HST, - copyArray(llvm::makeArrayRef(Attrs)), + S.copyArray(llvm::makeArrayRef(Attrs)), Tok.getLocation(), /* IsSelfClosing = */ true); consumeToken(); @@ -446,14 +470,14 @@ HTMLStartTagComment *Parser::parseHTMLStartTag() { continue; return S.actOnHTMLStartTagFinish(HST, - copyArray(llvm::makeArrayRef(Attrs)), + S.copyArray(llvm::makeArrayRef(Attrs)), SourceLocation(), /* IsSelfClosing = */ false); default: // Not a token from an HTML start tag. Thus HTML tag prematurely ended. HST = S.actOnHTMLStartTagFinish(HST, - copyArray(llvm::makeArrayRef(Attrs)), + S.copyArray(llvm::makeArrayRef(Attrs)), SourceLocation(), /* IsSelfClosing = */ false); bool StartLineInvalid; @@ -563,7 +587,7 @@ BlockContentComment *Parser::parseParagraphOrBlockCommand() { break; } - return S.actOnParagraphComment(copyArray(llvm::makeArrayRef(Content))); + return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content))); } VerbatimBlockComment *Parser::parseVerbatimBlock() { @@ -601,12 +625,12 @@ VerbatimBlockComment *Parser::parseVerbatimBlock() { if (Tok.is(tok::verbatim_block_end)) { VB = S.actOnVerbatimBlockFinish(VB, Tok.getLocation(), Tok.getVerbatimBlockName(), - copyArray(llvm::makeArrayRef(Lines))); + S.copyArray(llvm::makeArrayRef(Lines))); consumeToken(); } else { // Unterminated \\verbatim block VB = S.actOnVerbatimBlockFinish(VB, SourceLocation(), "", - copyArray(llvm::makeArrayRef(Lines))); + S.copyArray(llvm::makeArrayRef(Lines))); } return VB; @@ -680,7 +704,7 @@ FullComment *Parser::parseFullComment() { while (Tok.is(tok::newline)) consumeToken(); } - return S.actOnFullComment(copyArray(llvm::makeArrayRef(Blocks))); + return S.actOnFullComment(S.copyArray(llvm::makeArrayRef(Blocks))); } } // end namespace comments diff --git a/lib/AST/CommentSema.cpp b/lib/AST/CommentSema.cpp index bb0d03ebd2..7b42c861d3 100644 --- a/lib/AST/CommentSema.cpp +++ b/lib/AST/CommentSema.cpp @@ -11,6 +11,7 @@ #include "clang/AST/CommentDiagnostic.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" #include "clang/Basic/SourceManager.h" #include "llvm/ADT/StringSwitch.h" @@ -200,6 +201,90 @@ ParamCommandComment *Sema::actOnParamCommandFinish(ParamCommandComment *Command, return Command; } +TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin, + SourceLocation LocEnd, + StringRef Name) { + TParamCommandComment *Command = + new (Allocator) TParamCommandComment(LocBegin, LocEnd, Name); + + if (!isTemplateDecl()) + Diag(Command->getLocation(), + diag::warn_doc_tparam_not_attached_to_a_template_decl) + << Command->getCommandNameRange(); + + return Command; +} + +TParamCommandComment *Sema::actOnTParamCommandParamNameArg( + TParamCommandComment *Command, + SourceLocation ArgLocBegin, + SourceLocation ArgLocEnd, + StringRef Arg) { + // Parser will not feed us more arguments than needed. + assert(Command->getNumArgs() == 0); + + typedef BlockCommandComment::Argument Argument; + Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin, + ArgLocEnd), + Arg); + Command->setArgs(llvm::makeArrayRef(A, 1)); + + if (!isTemplateDecl()) { + // We already warned that this \\tparam is not attached to a template decl. + return Command; + } + + SmallVector<unsigned, 2> Position; + if (resolveTParamReference(Arg, TemplateParameters, &Position)) { + Command->setPosition(copyArray(llvm::makeArrayRef(Position))); + llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt = + TemplateParameterDocs.find(Arg); + if (PrevCommandIt != TemplateParameterDocs.end()) { + SourceRange ArgRange(ArgLocBegin, ArgLocEnd); + Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate) + << Arg << ArgRange; + TParamCommandComment *PrevCommand = PrevCommandIt->second; + Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous) + << PrevCommand->getParamNameRange(); + } + TemplateParameterDocs[Arg] = Command; + return Command; + } + + SourceRange ArgRange(ArgLocBegin, ArgLocEnd); + Diag(ArgLocBegin, diag::warn_doc_tparam_not_found) + << Arg << ArgRange; + + if (!TemplateParameters || TemplateParameters->size() == 0) + return Command; + + StringRef CorrectedName; + if (TemplateParameters->size() == 1) { + const NamedDecl *Param = TemplateParameters->getParam(0); + const IdentifierInfo *II = Param->getIdentifier(); + if (II) + CorrectedName = II->getName(); + } else { + CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters); + } + + if (!CorrectedName.empty()) { + Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion) + << CorrectedName + << FixItHint::CreateReplacement(ArgRange, CorrectedName); + } + + return Command; +} + +TParamCommandComment *Sema::actOnTParamCommandFinish( + TParamCommandComment *Command, + ParagraphComment *Paragraph) { + Command->setParagraph(Paragraph); + checkBlockCommandEmptyParagraph(Command); + return Command; +} + InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin, SourceLocation CommandLocEnd, StringRef CommandName) { @@ -387,6 +472,12 @@ bool Sema::isFunctionDecl() { return IsFunctionDecl; } +bool Sema::isTemplateDecl() { + if (!IsThisDeclInspected) + inspectThisDecl(); + return IsTemplateDecl; +} + ArrayRef<const ParmVarDecl *> Sema::getParamVars() { if (!IsThisDeclInspected) inspectThisDecl(); @@ -397,18 +488,56 @@ void Sema::inspectThisDecl() { assert(!IsThisDeclInspected); if (!ThisDecl) { IsFunctionDecl = false; + IsTemplateDecl = false; ParamVars = ArrayRef<const ParmVarDecl *>(); + TemplateParameters = NULL; } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ThisDecl)) { IsFunctionDecl = true; + IsTemplateDecl = false; ParamVars = ArrayRef<const ParmVarDecl *>(FD->param_begin(), FD->getNumParams()); + TemplateParameters = NULL; + unsigned NumLists = FD->getNumTemplateParameterLists(); + if (NumLists != 0) { + IsTemplateDecl = true; + TemplateParameters = FD->getTemplateParameterList(NumLists - 1); + } } else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ThisDecl)) { IsFunctionDecl = true; + IsTemplateDecl = false; ParamVars = ArrayRef<const ParmVarDecl *>(MD->param_begin(), MD->param_size()); + TemplateParameters = NULL; + } else if (const FunctionTemplateDecl *FTD = + dyn_cast<FunctionTemplateDecl>(ThisDecl)) { + IsFunctionDecl = true; + IsTemplateDecl = true; + const FunctionDecl *FD = FTD->getTemplatedDecl(); + ParamVars = ArrayRef<const ParmVarDecl *>(FD->param_begin(), + FD->getNumParams()); + TemplateParameters = FTD->getTemplateParameters(); + } else if (const ClassTemplateDecl *CTD = + dyn_cast<ClassTemplateDecl>(ThisDecl)) { + IsFunctionDecl = false; + IsTemplateDecl = true; + ParamVars = ArrayRef<const ParmVarDecl *>(); + TemplateParameters = CTD->getTemplateParameters(); + } else if (const ClassTemplatePartialSpecializationDecl *CTPSD = + dyn_cast<ClassTemplatePartialSpecializationDecl>(ThisDecl)) { + IsFunctionDecl = false; + IsTemplateDecl = true; + ParamVars = ArrayRef<const ParmVarDecl *>(); + TemplateParameters = CTPSD->getTemplateParameters(); + } else if (isa<ClassTemplateSpecializationDecl>(ThisDecl)) { + IsFunctionDecl = false; + IsTemplateDecl = true; + ParamVars = ArrayRef<const ParmVarDecl *>(); + TemplateParameters = NULL; } else { IsFunctionDecl = false; + IsTemplateDecl = false; ParamVars = ArrayRef<const ParmVarDecl *>(); + TemplateParameters = NULL; } ParamVarDocs.resize(ParamVars.size(), NULL); IsThisDeclInspected = true; @@ -424,34 +553,136 @@ unsigned Sema::resolveParmVarReference(StringRef Name, return ParamCommandComment::InvalidParamIndex; } +namespace { +class SimpleTypoCorrector { + StringRef Typo; + const unsigned MaxEditDistance; + + const NamedDecl *BestDecl; + unsigned BestEditDistance; + unsigned BestIndex; + unsigned NextIndex; + +public: + SimpleTypoCorrector(StringRef Typo) : + Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3), + BestDecl(NULL), BestEditDistance(MaxEditDistance + 1), + BestIndex(0), NextIndex(0) + { } + + void addDecl(const NamedDecl *ND); + + const NamedDecl *getBestDecl() const { + if (BestEditDistance > MaxEditDistance) + return NULL; + + return BestDecl; + } + + unsigned getBestDeclIndex() const { + assert(getBestDecl()); + return BestIndex; + } +}; + +void SimpleTypoCorrector::addDecl(const NamedDecl *ND) { + unsigned CurrIndex = NextIndex++; + + const IdentifierInfo *II = ND->getIdentifier(); + if (!II) + return; + + StringRef Name = II->getName(); + unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size()); + if (MinPossibleEditDistance > 0 && + Typo.size() / MinPossibleEditDistance < 3) + return; + + unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance); + if (EditDistance < BestEditDistance) { + BestEditDistance = EditDistance; + BestDecl = ND; + BestIndex = CurrIndex; + } +} +} // unnamed namespace + unsigned Sema::correctTypoInParmVarReference( StringRef Typo, ArrayRef<const ParmVarDecl *> ParamVars) { - const unsigned MaxEditDistance = (Typo.size() + 2) / 3; - unsigned BestPVDIndex = 0; - unsigned BestEditDistance = MaxEditDistance + 1; - for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) { - const IdentifierInfo *II = ParamVars[i]->getIdentifier(); - if (II) { - StringRef Name = II->getName(); - unsigned MinPossibleEditDistance = - abs((int)Name.size() - (int)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; - } + SimpleTypoCorrector Corrector(Typo); + for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) + Corrector.addDecl(ParamVars[i]); + if (Corrector.getBestDecl()) + return Corrector.getBestDeclIndex(); + else + return ParamCommandComment::InvalidParamIndex;; +} + +namespace { +bool ResolveTParamReferenceHelper( + StringRef Name, + const TemplateParameterList *TemplateParameters, + SmallVectorImpl<unsigned> *Position) { + for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) { + const NamedDecl *Param = TemplateParameters->getParam(i); + const IdentifierInfo *II = Param->getIdentifier(); + if (II && II->getName() == Name) { + Position->push_back(i); + return true; + } + + if (const TemplateTemplateParmDecl *TTP = + dyn_cast<TemplateTemplateParmDecl>(Param)) { + Position->push_back(i); + if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(), + Position)) + return true; + Position->pop_back(); } } + return false; +} +} // unnamed namespace - if (BestEditDistance <= MaxEditDistance) - return BestPVDIndex; - else - return ParamCommandComment::InvalidParamIndex; +bool Sema::resolveTParamReference( + StringRef Name, + const TemplateParameterList *TemplateParameters, + SmallVectorImpl<unsigned> *Position) { + Position->clear(); + if (!TemplateParameters) + return false; + + return ResolveTParamReferenceHelper(Name, TemplateParameters, Position); +} + +namespace { +void CorrectTypoInTParamReferenceHelper( + const TemplateParameterList *TemplateParameters, + SimpleTypoCorrector &Corrector) { + for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) { + const NamedDecl *Param = TemplateParameters->getParam(i); + Corrector.addDecl(Param); + + if (const TemplateTemplateParmDecl *TTP = + dyn_cast<TemplateTemplateParmDecl>(Param)) + CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(), + Corrector); + } +} +} // unnamed namespace + +StringRef Sema::correctTypoInTParamReference( + StringRef Typo, + const TemplateParameterList *TemplateParameters) { + SimpleTypoCorrector Corrector(Typo); + CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector); + if (const NamedDecl *ND = Corrector.getBestDecl()) { + const IdentifierInfo *II = ND->getIdentifier(); + assert(II && "SimpleTypoCorrector should not return this decl"); + return II->getName(); + } + return StringRef(); } // TODO: tablegen @@ -465,7 +696,7 @@ bool Sema::isBlockCommand(StringRef Name) { .Case("authors", true) .Case("pre", true) .Case("post", true) - .Default(false) || isParamCommand(Name); + .Default(false) || isParamCommand(Name) || isTParamCommand(Name); } bool Sema::isParamCommand(StringRef Name) { @@ -475,6 +706,10 @@ bool Sema::isParamCommand(StringRef Name) { .Default(false); } +bool Sema::isTParamCommand(StringRef Name) { + return Name == "tparam"; +} + unsigned Sema::getBlockCommandNumArgs(StringRef Name) { return llvm::StringSwitch<unsigned>(Name) .Cases("brief", "short", 0) diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index e511157366..0063a37a08 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -7618,6 +7618,7 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D) { << FD->getName() << "dllimport"; } } + ActOnDocumentableDecl(FD); return FD; } diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 1c3feb51eb..cbfa1ba42e 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -1139,6 +1139,8 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK, if (PrevClassTemplate) mergeDeclAttributes(NewClass, PrevClassTemplate->getTemplatedDecl()); + ActOnDocumentableDecl(NewTemplate); + return NewTemplate; } @@ -5568,7 +5570,9 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, Decl *Sema::ActOnTemplateDeclarator(Scope *S, MultiTemplateParamsArg TemplateParameterLists, Declarator &D) { - return HandleDeclarator(S, D, move(TemplateParameterLists)); + Decl *NewDecl = HandleDeclarator(S, D, move(TemplateParameterLists)); + ActOnDocumentableDecl(NewDecl); + return NewDecl; } Decl *Sema::ActOnStartOfFunctionTemplateDef(Scope *FnBodyScope, |