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
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 46a4d87..edcfe8e 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -72,6 +72,13 @@
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 @@
// 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 47d52e1..22209c0 100644
--- a/lib/AST/CommentBriefParser.cpp
+++ b/lib/AST/CommentBriefParser.cpp
@@ -48,6 +48,7 @@
.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 f02ea33..dffc823 100644
--- a/lib/AST/CommentDumper.cpp
+++ b/lib/AST/CommentDumper.cpp
@@ -50,6 +50,7 @@
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 @@
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 6d53567..63560e1 100644
--- a/lib/AST/CommentParser.cpp
+++ b/lib/AST/CommentParser.cpp
@@ -276,6 +276,19 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
break;
}
- return S.actOnParagraphComment(copyArray(llvm::makeArrayRef(Content)));
+ return S.actOnParagraphComment(S.copyArray(llvm::makeArrayRef(Content)));
}
VerbatimBlockComment *Parser::parseVerbatimBlock() {
@@ -601,12 +625,12 @@
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 @@
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 bb0d03e..7b42c86 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 @@
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 @@
return IsFunctionDecl;
}
+bool Sema::isTemplateDecl() {
+ if (!IsThisDeclInspected)
+ inspectThisDecl();
+ return IsTemplateDecl;
+}
+
ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
if (!IsThisDeclInspected)
inspectThisDecl();
@@ -397,18 +488,56 @@
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 @@
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;
+ 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;;
+}
- unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
- if (EditDistance < BestEditDistance) {
- BestEditDistance = EditDistance;
- BestPVDIndex = i;
- }
+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 @@
.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 @@
.Default(false);
}
+bool Sema::isTParamCommand(StringRef Name) {
+ return Name == "tparam";
+}
+
unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
return llvm::StringSwitch<unsigned>(Name)
.Cases("brief", "short", 0)