2012-07-06 08:28:32 +08:00
|
|
|
//===--- CommentSema.cpp - Doxygen comment semantic analysis --------------===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "clang/AST/CommentSema.h"
|
2012-07-12 05:38:39 +08:00
|
|
|
#include "clang/AST/CommentDiagnostic.h"
|
|
|
|
#include "clang/AST/Decl.h"
|
2012-08-01 06:37:06 +08:00
|
|
|
#include "clang/AST/DeclTemplate.h"
|
2012-07-12 05:38:39 +08:00
|
|
|
#include "clang/Basic/SourceManager.h"
|
2012-07-06 08:28:32 +08:00
|
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace comments {
|
|
|
|
|
2012-07-12 05:38:39 +08:00
|
|
|
Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
|
|
|
|
DiagnosticsEngine &Diags) :
|
2012-08-02 07:08:09 +08:00
|
|
|
Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
|
|
|
|
ThisDeclInfo(NULL) {
|
2012-07-12 05:38:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sema::setDecl(const Decl *D) {
|
2012-08-02 07:08:09 +08:00
|
|
|
if (!D)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ThisDeclInfo = new (Allocator) DeclInfo;
|
|
|
|
ThisDeclInfo->ThisDecl = D;
|
2012-08-02 07:21:57 +08:00
|
|
|
ThisDeclInfo->IsFilled = false;
|
2012-07-06 08:28:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ParagraphComment *Sema::actOnParagraphComment(
|
|
|
|
ArrayRef<InlineContentComment *> Content) {
|
|
|
|
return new (Allocator) ParagraphComment(Content);
|
|
|
|
}
|
|
|
|
|
|
|
|
BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin,
|
|
|
|
SourceLocation LocEnd,
|
|
|
|
StringRef Name) {
|
|
|
|
return new (Allocator) BlockCommandComment(LocBegin, LocEnd, Name);
|
|
|
|
}
|
|
|
|
|
|
|
|
BlockCommandComment *Sema::actOnBlockCommandArgs(
|
|
|
|
BlockCommandComment *Command,
|
|
|
|
ArrayRef<BlockCommandComment::Argument> Args) {
|
|
|
|
Command->setArgs(Args);
|
|
|
|
return Command;
|
|
|
|
}
|
|
|
|
|
|
|
|
BlockCommandComment *Sema::actOnBlockCommandFinish(
|
|
|
|
BlockCommandComment *Command,
|
|
|
|
ParagraphComment *Paragraph) {
|
|
|
|
Command->setParagraph(Paragraph);
|
2012-07-12 05:38:39 +08:00
|
|
|
checkBlockCommandEmptyParagraph(Command);
|
2012-07-06 08:28:32 +08:00
|
|
|
return Command;
|
|
|
|
}
|
|
|
|
|
|
|
|
ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
|
|
|
|
SourceLocation LocEnd,
|
|
|
|
StringRef Name) {
|
2012-07-12 05:38:39 +08:00
|
|
|
ParamCommandComment *Command =
|
|
|
|
new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
|
|
|
|
|
2012-07-24 01:40:30 +08:00
|
|
|
if (!isFunctionDecl())
|
2012-07-12 05:38:39 +08:00
|
|
|
Diag(Command->getLocation(),
|
|
|
|
diag::warn_doc_param_not_attached_to_a_function_decl)
|
|
|
|
<< Command->getCommandNameRange();
|
|
|
|
|
|
|
|
return Command;
|
2012-07-06 08:28:32 +08:00
|
|
|
}
|
|
|
|
|
2012-07-12 05:38:39 +08:00
|
|
|
ParamCommandComment *Sema::actOnParamCommandDirectionArg(
|
|
|
|
ParamCommandComment *Command,
|
2012-07-06 08:28:32 +08:00
|
|
|
SourceLocation ArgLocBegin,
|
|
|
|
SourceLocation ArgLocEnd,
|
2012-07-12 05:38:39 +08:00
|
|
|
StringRef Arg) {
|
|
|
|
ParamCommandComment::PassDirection Direction;
|
|
|
|
std::string ArgLower = Arg.lower();
|
|
|
|
// TODO: optimize: lower Name first (need an API in SmallString for that),
|
|
|
|
// after that StringSwitch.
|
|
|
|
if (ArgLower == "[in]")
|
|
|
|
Direction = ParamCommandComment::In;
|
|
|
|
else if (ArgLower == "[out]")
|
|
|
|
Direction = ParamCommandComment::Out;
|
|
|
|
else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
|
|
|
|
Direction = ParamCommandComment::InOut;
|
|
|
|
else {
|
|
|
|
// Remove spaces.
|
|
|
|
std::string::iterator O = ArgLower.begin();
|
|
|
|
for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
|
|
|
|
I != E; ++I) {
|
|
|
|
const char C = *I;
|
|
|
|
if (C != ' ' && C != '\n' && C != '\r' &&
|
|
|
|
C != '\t' && C != '\v' && C != '\f')
|
|
|
|
*O++ = C;
|
|
|
|
}
|
|
|
|
ArgLower.resize(O - ArgLower.begin());
|
|
|
|
|
|
|
|
bool RemovingWhitespaceHelped = false;
|
|
|
|
if (ArgLower == "[in]") {
|
2012-07-06 08:28:32 +08:00
|
|
|
Direction = ParamCommandComment::In;
|
2012-07-12 05:38:39 +08:00
|
|
|
RemovingWhitespaceHelped = true;
|
|
|
|
} else if (ArgLower == "[out]") {
|
2012-07-06 08:28:32 +08:00
|
|
|
Direction = ParamCommandComment::Out;
|
2012-07-12 05:38:39 +08:00
|
|
|
RemovingWhitespaceHelped = true;
|
|
|
|
} else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
|
2012-07-06 08:28:32 +08:00
|
|
|
Direction = ParamCommandComment::InOut;
|
2012-07-12 05:38:39 +08:00
|
|
|
RemovingWhitespaceHelped = true;
|
2012-07-06 08:28:32 +08:00
|
|
|
} else {
|
2012-07-12 05:38:39 +08:00
|
|
|
Direction = ParamCommandComment::In;
|
|
|
|
RemovingWhitespaceHelped = false;
|
2012-07-06 08:28:32 +08:00
|
|
|
}
|
2012-07-12 05:38:39 +08:00
|
|
|
|
|
|
|
SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
|
|
|
|
if (RemovingWhitespaceHelped)
|
|
|
|
Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
|
|
|
|
<< ArgRange
|
|
|
|
<< FixItHint::CreateReplacement(
|
|
|
|
ArgRange,
|
|
|
|
ParamCommandComment::getDirectionAsString(Direction));
|
|
|
|
else
|
|
|
|
Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
|
|
|
|
<< ArgRange;
|
2012-07-06 08:28:32 +08:00
|
|
|
}
|
2012-07-12 05:38:39 +08:00
|
|
|
Command->setDirection(Direction, /* Explicit = */ true);
|
|
|
|
return Command;
|
|
|
|
}
|
|
|
|
|
|
|
|
ParamCommandComment *Sema::actOnParamCommandParamNameArg(
|
|
|
|
ParamCommandComment *Command,
|
|
|
|
SourceLocation ArgLocBegin,
|
|
|
|
SourceLocation ArgLocEnd,
|
|
|
|
StringRef Arg) {
|
|
|
|
// Parser will not feed us more arguments than needed.
|
2012-07-14 03:02:42 +08:00
|
|
|
assert(Command->getNumArgs() == 0);
|
2012-07-12 05:38:39 +08:00
|
|
|
|
|
|
|
if (!Command->isDirectionExplicit()) {
|
|
|
|
// User didn't provide a direction argument.
|
|
|
|
Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
|
|
|
|
}
|
|
|
|
typedef BlockCommandComment::Argument Argument;
|
|
|
|
Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
|
|
|
|
ArgLocEnd),
|
|
|
|
Arg);
|
|
|
|
Command->setArgs(llvm::makeArrayRef(A, 1));
|
|
|
|
|
2012-07-24 01:40:30 +08:00
|
|
|
if (!isFunctionDecl()) {
|
2012-07-12 05:38:39 +08:00
|
|
|
// We already warned that this \\param is not attached to a function decl.
|
|
|
|
return Command;
|
|
|
|
}
|
|
|
|
|
2012-07-24 01:40:30 +08:00
|
|
|
ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
|
|
|
|
|
2012-07-12 05:38:39 +08:00
|
|
|
// Check that referenced parameter name is in the function decl.
|
2012-07-24 01:40:30 +08:00
|
|
|
const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars);
|
2012-07-12 05:38:39 +08:00
|
|
|
if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
|
|
|
|
Command->setParamIndex(ResolvedParamIndex);
|
2012-07-25 05:44:16 +08:00
|
|
|
if (ParamVarDocs[ResolvedParamIndex]) {
|
|
|
|
SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
|
|
|
|
Diag(ArgLocBegin, diag::warn_doc_param_duplicate)
|
|
|
|
<< Arg << ArgRange;
|
|
|
|
ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
|
|
|
|
Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
|
|
|
|
<< PrevCommand->getParamNameRange();
|
|
|
|
}
|
|
|
|
ParamVarDocs[ResolvedParamIndex] = Command;
|
2012-07-12 05:38:39 +08:00
|
|
|
return Command;
|
|
|
|
}
|
|
|
|
|
|
|
|
SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
|
|
|
|
Diag(ArgLocBegin, diag::warn_doc_param_not_found)
|
|
|
|
<< Arg << ArgRange;
|
|
|
|
|
2012-07-28 05:34:43 +08:00
|
|
|
// No parameters -- can't suggest a correction.
|
|
|
|
if (ParamVars.size() == 0)
|
|
|
|
return Command;
|
|
|
|
|
2012-07-12 05:38:39 +08:00
|
|
|
unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
|
2012-07-24 01:40:30 +08:00
|
|
|
if (ParamVars.size() == 1) {
|
2012-07-12 05:38:39 +08:00
|
|
|
// If function has only one parameter then only that parameter
|
|
|
|
// can be documented.
|
|
|
|
CorrectedParamIndex = 0;
|
|
|
|
} else {
|
|
|
|
// Do typo correction.
|
2012-07-24 01:40:30 +08:00
|
|
|
CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars);
|
2012-07-12 05:38:39 +08:00
|
|
|
}
|
|
|
|
if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
|
|
|
|
const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex];
|
|
|
|
if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
|
|
|
|
Diag(ArgLocBegin, diag::note_doc_param_name_suggestion)
|
|
|
|
<< CorrectedII->getName()
|
|
|
|
<< FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
|
|
|
|
}
|
|
|
|
|
2012-07-06 08:28:32 +08:00
|
|
|
return Command;
|
|
|
|
}
|
|
|
|
|
|
|
|
ParamCommandComment *Sema::actOnParamCommandFinish(ParamCommandComment *Command,
|
|
|
|
ParagraphComment *Paragraph) {
|
|
|
|
Command->setParagraph(Paragraph);
|
2012-07-12 05:38:39 +08:00
|
|
|
checkBlockCommandEmptyParagraph(Command);
|
2012-07-06 08:28:32 +08:00
|
|
|
return Command;
|
|
|
|
}
|
|
|
|
|
2012-08-01 06:37:06 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-08-02 07:08:09 +08:00
|
|
|
const TemplateParameterList *TemplateParameters =
|
|
|
|
ThisDeclInfo->TemplateParameters;
|
2012-08-01 06:37:06 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-07-06 08:28:32 +08:00
|
|
|
InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
|
|
|
|
SourceLocation CommandLocEnd,
|
|
|
|
StringRef CommandName) {
|
|
|
|
ArrayRef<InlineCommandComment::Argument> Args;
|
2012-07-24 00:43:01 +08:00
|
|
|
return new (Allocator) InlineCommandComment(
|
|
|
|
CommandLocBegin,
|
|
|
|
CommandLocEnd,
|
|
|
|
CommandName,
|
|
|
|
getInlineCommandRenderKind(CommandName),
|
|
|
|
Args);
|
2012-07-06 08:28:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
|
|
|
|
SourceLocation CommandLocEnd,
|
|
|
|
StringRef CommandName,
|
|
|
|
SourceLocation ArgLocBegin,
|
|
|
|
SourceLocation ArgLocEnd,
|
|
|
|
StringRef Arg) {
|
|
|
|
typedef InlineCommandComment::Argument Argument;
|
|
|
|
Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
|
|
|
|
ArgLocEnd),
|
|
|
|
Arg);
|
|
|
|
|
2012-07-24 00:43:01 +08:00
|
|
|
return new (Allocator) InlineCommandComment(
|
|
|
|
CommandLocBegin,
|
|
|
|
CommandLocEnd,
|
|
|
|
CommandName,
|
|
|
|
getInlineCommandRenderKind(CommandName),
|
|
|
|
llvm::makeArrayRef(A, 1));
|
2012-07-06 08:28:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
|
|
|
|
SourceLocation LocEnd,
|
|
|
|
StringRef Name) {
|
|
|
|
ArrayRef<InlineCommandComment::Argument> Args;
|
2012-07-24 00:43:01 +08:00
|
|
|
return new (Allocator) InlineCommandComment(
|
|
|
|
LocBegin, LocEnd, Name,
|
|
|
|
InlineCommandComment::RenderNormal,
|
|
|
|
Args);
|
2012-07-06 08:28:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TextComment *Sema::actOnText(SourceLocation LocBegin,
|
|
|
|
SourceLocation LocEnd,
|
|
|
|
StringRef Text) {
|
|
|
|
return new (Allocator) TextComment(LocBegin, LocEnd, Text);
|
|
|
|
}
|
|
|
|
|
|
|
|
VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
|
|
|
|
StringRef Name) {
|
|
|
|
return new (Allocator) VerbatimBlockComment(
|
|
|
|
Loc,
|
|
|
|
Loc.getLocWithOffset(1 + Name.size()),
|
|
|
|
Name);
|
|
|
|
}
|
|
|
|
|
|
|
|
VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
|
|
|
|
StringRef Text) {
|
|
|
|
return new (Allocator) VerbatimBlockLineComment(Loc, Text);
|
|
|
|
}
|
|
|
|
|
|
|
|
VerbatimBlockComment *Sema::actOnVerbatimBlockFinish(
|
|
|
|
VerbatimBlockComment *Block,
|
|
|
|
SourceLocation CloseNameLocBegin,
|
|
|
|
StringRef CloseName,
|
|
|
|
ArrayRef<VerbatimBlockLineComment *> Lines) {
|
|
|
|
Block->setCloseName(CloseName, CloseNameLocBegin);
|
|
|
|
Block->setLines(Lines);
|
|
|
|
return Block;
|
|
|
|
}
|
|
|
|
|
|
|
|
VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
|
|
|
|
StringRef Name,
|
|
|
|
SourceLocation TextBegin,
|
|
|
|
StringRef Text) {
|
|
|
|
return new (Allocator) VerbatimLineComment(
|
|
|
|
LocBegin,
|
|
|
|
TextBegin.getLocWithOffset(Text.size()),
|
|
|
|
Name,
|
|
|
|
TextBegin,
|
|
|
|
Text);
|
|
|
|
}
|
|
|
|
|
2012-07-13 08:44:24 +08:00
|
|
|
HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
|
|
|
|
StringRef TagName) {
|
|
|
|
return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
|
2012-07-06 08:28:32 +08:00
|
|
|
}
|
|
|
|
|
2012-07-13 08:44:24 +08:00
|
|
|
HTMLStartTagComment *Sema::actOnHTMLStartTagFinish(
|
|
|
|
HTMLStartTagComment *Tag,
|
|
|
|
ArrayRef<HTMLStartTagComment::Attribute> Attrs,
|
2012-07-12 05:38:39 +08:00
|
|
|
SourceLocation GreaterLoc,
|
|
|
|
bool IsSelfClosing) {
|
2012-07-06 08:28:32 +08:00
|
|
|
Tag->setAttrs(Attrs);
|
|
|
|
Tag->setGreaterLoc(GreaterLoc);
|
2012-07-12 05:38:39 +08:00
|
|
|
if (IsSelfClosing)
|
|
|
|
Tag->setSelfClosing();
|
2012-07-13 08:44:24 +08:00
|
|
|
else if (!isHTMLEndTagForbidden(Tag->getTagName()))
|
2012-07-12 05:38:39 +08:00
|
|
|
HTMLOpenTags.push_back(Tag);
|
2012-07-06 08:28:32 +08:00
|
|
|
return Tag;
|
|
|
|
}
|
|
|
|
|
2012-07-13 08:44:24 +08:00
|
|
|
HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
|
|
|
|
SourceLocation LocEnd,
|
|
|
|
StringRef TagName) {
|
|
|
|
HTMLEndTagComment *HET =
|
|
|
|
new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
|
|
|
|
if (isHTMLEndTagForbidden(TagName)) {
|
|
|
|
Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
|
|
|
|
<< TagName << HET->getSourceRange();
|
|
|
|
return HET;
|
2012-07-13 07:37:09 +08:00
|
|
|
}
|
|
|
|
|
2012-07-12 05:38:39 +08:00
|
|
|
bool FoundOpen = false;
|
2012-07-13 08:44:24 +08:00
|
|
|
for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
|
2012-07-12 05:38:39 +08:00
|
|
|
I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
|
|
|
|
I != E; ++I) {
|
|
|
|
if ((*I)->getTagName() == TagName) {
|
|
|
|
FoundOpen = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!FoundOpen) {
|
2012-07-13 08:44:24 +08:00
|
|
|
Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
|
|
|
|
<< HET->getSourceRange();
|
|
|
|
return HET;
|
2012-07-12 05:38:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
while (!HTMLOpenTags.empty()) {
|
2012-07-13 08:44:24 +08:00
|
|
|
const HTMLStartTagComment *HST = HTMLOpenTags.back();
|
2012-07-12 05:38:39 +08:00
|
|
|
HTMLOpenTags.pop_back();
|
2012-07-13 08:44:24 +08:00
|
|
|
StringRef LastNotClosedTagName = HST->getTagName();
|
2012-07-12 05:38:39 +08:00
|
|
|
if (LastNotClosedTagName == TagName)
|
|
|
|
break;
|
|
|
|
|
2012-07-13 08:44:24 +08:00
|
|
|
if (isHTMLEndTagOptional(LastNotClosedTagName))
|
2012-07-12 05:38:39 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
bool OpenLineInvalid;
|
|
|
|
const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
|
2012-07-13 08:44:24 +08:00
|
|
|
HST->getLocation(),
|
2012-07-12 05:38:39 +08:00
|
|
|
&OpenLineInvalid);
|
|
|
|
bool CloseLineInvalid;
|
|
|
|
const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
|
2012-07-13 08:44:24 +08:00
|
|
|
HET->getLocation(),
|
2012-07-12 05:38:39 +08:00
|
|
|
&CloseLineInvalid);
|
|
|
|
|
|
|
|
if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
|
2012-07-13 08:44:24 +08:00
|
|
|
Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
|
|
|
|
<< HST->getTagName() << HET->getTagName()
|
|
|
|
<< HST->getSourceRange() << HET->getSourceRange();
|
2012-07-12 05:38:39 +08:00
|
|
|
else {
|
2012-07-13 08:44:24 +08:00
|
|
|
Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
|
|
|
|
<< HST->getTagName() << HET->getTagName()
|
|
|
|
<< HST->getSourceRange();
|
|
|
|
Diag(HET->getLocation(), diag::note_doc_html_end_tag)
|
|
|
|
<< HET->getSourceRange();
|
2012-07-12 05:38:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-13 08:44:24 +08:00
|
|
|
return HET;
|
2012-07-06 08:28:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
FullComment *Sema::actOnFullComment(
|
|
|
|
ArrayRef<BlockContentComment *> Blocks) {
|
2012-08-02 07:08:09 +08:00
|
|
|
return new (Allocator) FullComment(Blocks, ThisDeclInfo);
|
2012-07-06 08:28:32 +08:00
|
|
|
}
|
|
|
|
|
2012-07-12 05:38:39 +08:00
|
|
|
void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
|
|
|
|
ParagraphComment *Paragraph = Command->getParagraph();
|
|
|
|
if (Paragraph->isWhitespace()) {
|
|
|
|
SourceLocation DiagLoc;
|
2012-07-14 03:02:42 +08:00
|
|
|
if (Command->getNumArgs() > 0)
|
|
|
|
DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
|
2012-07-12 05:38:39 +08:00
|
|
|
if (!DiagLoc.isValid())
|
|
|
|
DiagLoc = Command->getCommandNameRange().getEnd();
|
|
|
|
Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
|
|
|
|
<< Command->getCommandName()
|
|
|
|
<< Command->getSourceRange();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-24 01:40:30 +08:00
|
|
|
bool Sema::isFunctionDecl() {
|
2012-08-02 07:08:09 +08:00
|
|
|
if (!ThisDeclInfo)
|
|
|
|
return false;
|
|
|
|
if (!ThisDeclInfo->IsFilled)
|
2012-07-25 04:58:46 +08:00
|
|
|
inspectThisDecl();
|
2012-08-03 05:45:39 +08:00
|
|
|
return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
|
2012-07-24 01:40:30 +08:00
|
|
|
}
|
|
|
|
|
2012-08-01 06:37:06 +08:00
|
|
|
bool Sema::isTemplateDecl() {
|
2012-08-02 07:08:09 +08:00
|
|
|
if (!ThisDeclInfo)
|
|
|
|
return false;
|
|
|
|
if (!ThisDeclInfo->IsFilled)
|
2012-08-01 06:37:06 +08:00
|
|
|
inspectThisDecl();
|
2012-08-02 07:08:09 +08:00
|
|
|
return ThisDeclInfo->IsTemplateDecl;
|
2012-08-01 06:37:06 +08:00
|
|
|
}
|
|
|
|
|
2012-07-24 01:40:30 +08:00
|
|
|
ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
|
2012-08-02 07:08:09 +08:00
|
|
|
if (!ThisDeclInfo->IsFilled)
|
2012-07-25 04:58:46 +08:00
|
|
|
inspectThisDecl();
|
2012-08-02 07:08:09 +08:00
|
|
|
return ThisDeclInfo->ParamVars;
|
2012-07-24 01:40:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Sema::inspectThisDecl() {
|
2012-08-02 07:08:09 +08:00
|
|
|
ThisDeclInfo->fill();
|
|
|
|
ParamVarDocs.resize(ThisDeclInfo->ParamVars.size(), NULL);
|
2012-07-24 01:40:30 +08:00
|
|
|
}
|
|
|
|
|
2012-07-12 05:38:39 +08:00
|
|
|
unsigned Sema::resolveParmVarReference(StringRef Name,
|
2012-07-24 01:40:30 +08:00
|
|
|
ArrayRef<const ParmVarDecl *> ParamVars) {
|
|
|
|
for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
|
2012-07-12 05:38:39 +08:00
|
|
|
const IdentifierInfo *II = ParamVars[i]->getIdentifier();
|
|
|
|
if (II && II->getName() == Name)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return ParamCommandComment::InvalidParamIndex;
|
|
|
|
}
|
|
|
|
|
2012-08-01 06:37:06 +08:00
|
|
|
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
|
|
|
|
|
2012-07-12 05:38:39 +08:00
|
|
|
unsigned Sema::correctTypoInParmVarReference(
|
|
|
|
StringRef Typo,
|
2012-07-24 01:40:30 +08:00
|
|
|
ArrayRef<const ParmVarDecl *> ParamVars) {
|
2012-08-01 06:37:06 +08:00
|
|
|
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();
|
2012-07-12 05:38:39 +08:00
|
|
|
}
|
|
|
|
}
|
2012-08-01 06:37:06 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} // unnamed namespace
|
2012-07-12 05:38:39 +08:00
|
|
|
|
2012-08-01 06:37:06 +08:00
|
|
|
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();
|
2012-07-12 05:38:39 +08:00
|
|
|
}
|
|
|
|
|
2012-07-06 08:28:32 +08:00
|
|
|
// TODO: tablegen
|
|
|
|
bool Sema::isBlockCommand(StringRef Name) {
|
|
|
|
return llvm::StringSwitch<bool>(Name)
|
2012-07-18 08:44:55 +08:00
|
|
|
.Cases("brief", "short", true)
|
2012-07-06 08:28:32 +08:00
|
|
|
.Case("result", true)
|
|
|
|
.Case("return", true)
|
|
|
|
.Case("returns", true)
|
|
|
|
.Case("author", true)
|
|
|
|
.Case("authors", true)
|
|
|
|
.Case("pre", true)
|
|
|
|
.Case("post", true)
|
2012-08-01 06:37:06 +08:00
|
|
|
.Default(false) || isParamCommand(Name) || isTParamCommand(Name);
|
2012-07-06 08:28:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Sema::isParamCommand(StringRef Name) {
|
|
|
|
return llvm::StringSwitch<bool>(Name)
|
|
|
|
.Case("param", true)
|
|
|
|
.Case("arg", true)
|
|
|
|
.Default(false);
|
|
|
|
}
|
|
|
|
|
2012-08-01 06:37:06 +08:00
|
|
|
bool Sema::isTParamCommand(StringRef Name) {
|
|
|
|
return Name == "tparam";
|
|
|
|
}
|
|
|
|
|
2012-07-06 08:28:32 +08:00
|
|
|
unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
|
|
|
|
return llvm::StringSwitch<unsigned>(Name)
|
2012-07-18 08:44:55 +08:00
|
|
|
.Cases("brief", "short", 0)
|
2012-07-06 08:28:32 +08:00
|
|
|
.Case("pre", 0)
|
|
|
|
.Case("post", 0)
|
|
|
|
.Case("author", 0)
|
|
|
|
.Case("authors", 0)
|
|
|
|
.Default(0);
|
|
|
|
}
|
|
|
|
|
2012-07-24 00:43:01 +08:00
|
|
|
bool Sema::isInlineCommand(StringRef Name) const {
|
2012-07-06 08:28:32 +08:00
|
|
|
return llvm::StringSwitch<bool>(Name)
|
2012-07-19 08:21:03 +08:00
|
|
|
.Case("b", true)
|
|
|
|
.Cases("c", "p", true)
|
|
|
|
.Cases("a", "e", "em", true)
|
2012-07-06 08:28:32 +08:00
|
|
|
.Default(false);
|
|
|
|
}
|
|
|
|
|
2012-07-24 00:43:01 +08:00
|
|
|
InlineCommandComment::RenderKind
|
|
|
|
Sema::getInlineCommandRenderKind(StringRef Name) const {
|
|
|
|
assert(isInlineCommand(Name));
|
|
|
|
|
|
|
|
return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
|
|
|
|
.Case("b", InlineCommandComment::RenderBold)
|
|
|
|
.Cases("c", "p", InlineCommandComment::RenderMonospaced)
|
|
|
|
.Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
|
|
|
|
.Default(InlineCommandComment::RenderNormal);
|
|
|
|
}
|
|
|
|
|
2012-07-13 08:44:24 +08:00
|
|
|
bool Sema::isHTMLEndTagOptional(StringRef Name) {
|
2012-07-06 08:28:32 +08:00
|
|
|
return llvm::StringSwitch<bool>(Name)
|
2012-07-13 07:37:09 +08:00
|
|
|
.Case("p", true)
|
|
|
|
.Case("li", true)
|
|
|
|
.Case("dt", true)
|
|
|
|
.Case("dd", true)
|
|
|
|
.Case("tr", true)
|
|
|
|
.Case("th", true)
|
|
|
|
.Case("td", true)
|
|
|
|
.Case("thead", true)
|
|
|
|
.Case("tfoot", true)
|
|
|
|
.Case("tbody", true)
|
|
|
|
.Case("colgroup", true)
|
|
|
|
.Default(false);
|
|
|
|
}
|
|
|
|
|
2012-07-13 08:44:24 +08:00
|
|
|
bool Sema::isHTMLEndTagForbidden(StringRef Name) {
|
2012-07-13 07:37:09 +08:00
|
|
|
return llvm::StringSwitch<bool>(Name)
|
|
|
|
.Case("br", true)
|
|
|
|
.Case("hr", true)
|
|
|
|
.Case("img", true)
|
|
|
|
.Case("col", true)
|
|
|
|
.Default(false);
|
2012-07-06 08:28:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
} // end namespace comments
|
|
|
|
} // end namespace clang
|
|
|
|
|