diff options
author | Dmitri Gribenko <gribozavr@gmail.com> | 2012-07-31 22:37:06 +0000 |
---|---|---|
committer | Dmitri Gribenko <gribozavr@gmail.com> | 2012-07-31 22:37:06 +0000 |
commit | 96b098674908eaa59a9128f3305cda6fbbdad563 (patch) | |
tree | d57eea0d73104b14e3bd09e070657d2af8a29edd /lib/AST/CommentSema.cpp | |
parent | 3c394c54511b27be0ff6968f159bba3521ab3c3e (diff) |
Comment parsing: add support for \tparam command on all levels.
The only caveat is renumbering CXCommentKind enum for aesthetic reasons -- this
breaks libclang binary compatibility, but should not be a problem since API is
so new.
This also fixes PR13372 as a side-effect.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@161087 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/AST/CommentSema.cpp')
-rw-r--r-- | lib/AST/CommentSema.cpp | 281 |
1 files changed, 258 insertions, 23 deletions
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) |