summaryrefslogtreecommitdiffstats
path: root/lib/AST/CommentSema.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/AST/CommentSema.cpp')
-rw-r--r--lib/AST/CommentSema.cpp269
1 files changed, 250 insertions, 19 deletions
diff --git a/lib/AST/CommentSema.cpp b/lib/AST/CommentSema.cpp
index 08ecb3a..e0138d5 100644
--- a/lib/AST/CommentSema.cpp
+++ b/lib/AST/CommentSema.cpp
@@ -8,14 +8,15 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/CommentSema.h"
-#include "clang/AST/CommentDiagnostic.h"
+#include "clang/AST/Attr.h"
#include "clang/AST/CommentCommandTraits.h"
+#include "clang/AST/CommentDiagnostic.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Preprocessor.h"
-#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringSwitch.h"
namespace clang {
namespace comments {
@@ -28,7 +29,8 @@ Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
DiagnosticsEngine &Diags, CommandTraits &Traits,
const Preprocessor *PP) :
Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits),
- PP(PP), ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) {
+ PP(PP), ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL),
+ HeaderfileCommand(NULL) {
}
void Sema::setDecl(const Decl *D) {
@@ -45,10 +47,16 @@ ParagraphComment *Sema::actOnParagraphComment(
return new (Allocator) ParagraphComment(Content);
}
-BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin,
- SourceLocation LocEnd,
- unsigned CommandID) {
- return new (Allocator) BlockCommandComment(LocBegin, LocEnd, CommandID);
+BlockCommandComment *Sema::actOnBlockCommandStart(
+ SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ unsigned CommandID,
+ CommandMarkerKind CommandMarker) {
+ BlockCommandComment *BC = new (Allocator) BlockCommandComment(LocBegin, LocEnd,
+ CommandID,
+ CommandMarker);
+ checkContainerDecl(BC);
+ return BC;
}
void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
@@ -65,20 +73,139 @@ void Sema::actOnBlockCommandFinish(BlockCommandComment *Command,
checkDeprecatedCommand(Command);
}
-ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
- SourceLocation LocEnd,
- unsigned CommandID) {
+ParamCommandComment *Sema::actOnParamCommandStart(
+ SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ unsigned CommandID,
+ CommandMarkerKind CommandMarker) {
ParamCommandComment *Command =
- new (Allocator) ParamCommandComment(LocBegin, LocEnd, CommandID);
+ new (Allocator) ParamCommandComment(LocBegin, LocEnd, CommandID,
+ CommandMarker);
if (!isFunctionDecl())
Diag(Command->getLocation(),
diag::warn_doc_param_not_attached_to_a_function_decl)
+ << CommandMarker
<< Command->getCommandNameRange(Traits);
return Command;
}
+void Sema::checkFunctionDeclVerbatimLine(const BlockCommandComment *Comment) {
+ const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
+ if (!Info->IsFunctionDeclarationCommand)
+ return;
+
+ unsigned DiagSelect;
+ switch (Comment->getCommandID()) {
+ case CommandTraits::KCI_function:
+ DiagSelect = !isAnyFunctionDecl() ? 1 : 0;
+ break;
+ case CommandTraits::KCI_functiongroup:
+ DiagSelect = !isAnyFunctionDecl() ? 2 : 0;
+ break;
+ case CommandTraits::KCI_method:
+ DiagSelect = !isObjCMethodDecl() ? 3 : 0;
+ break;
+ case CommandTraits::KCI_methodgroup:
+ DiagSelect = !isObjCMethodDecl() ? 4 : 0;
+ break;
+ case CommandTraits::KCI_callback:
+ DiagSelect = !isFunctionPointerVarDecl() ? 5 : 0;
+ break;
+ default:
+ DiagSelect = 0;
+ break;
+ }
+ if (DiagSelect)
+ Diag(Comment->getLocation(), diag::warn_doc_function_method_decl_mismatch)
+ << Comment->getCommandMarker()
+ << (DiagSelect-1) << (DiagSelect-1)
+ << Comment->getSourceRange();
+}
+
+void Sema::checkContainerDeclVerbatimLine(const BlockCommandComment *Comment) {
+ const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
+ if (!Info->IsRecordLikeDeclarationCommand)
+ return;
+ unsigned DiagSelect;
+ switch (Comment->getCommandID()) {
+ case CommandTraits::KCI_class:
+ DiagSelect = !isClassOrStructDecl() ? 1 : 0;
+ break;
+ case CommandTraits::KCI_interface:
+ DiagSelect = !isObjCInterfaceDecl() ? 2 : 0;
+ break;
+ case CommandTraits::KCI_protocol:
+ DiagSelect = !isObjCProtocolDecl() ? 3 : 0;
+ break;
+ case CommandTraits::KCI_struct:
+ DiagSelect = !isClassOrStructDecl() ? 4 : 0;
+ break;
+ case CommandTraits::KCI_union:
+ DiagSelect = !isUnionDecl() ? 5 : 0;
+ break;
+ default:
+ DiagSelect = 0;
+ break;
+ }
+ if (DiagSelect)
+ Diag(Comment->getLocation(), diag::warn_doc_api_container_decl_mismatch)
+ << Comment->getCommandMarker()
+ << (DiagSelect-1) << (DiagSelect-1)
+ << Comment->getSourceRange();
+}
+
+void Sema::checkContainerDecl(const BlockCommandComment *Comment) {
+ const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
+ if (!Info->IsRecordLikeDetailCommand || isRecordLikeDecl())
+ return;
+ unsigned DiagSelect;
+ switch (Comment->getCommandID()) {
+ case CommandTraits::KCI_classdesign:
+ DiagSelect = 1;
+ break;
+ case CommandTraits::KCI_coclass:
+ DiagSelect = 2;
+ break;
+ case CommandTraits::KCI_dependency:
+ DiagSelect = 3;
+ break;
+ case CommandTraits::KCI_helper:
+ DiagSelect = 4;
+ break;
+ case CommandTraits::KCI_helperclass:
+ DiagSelect = 5;
+ break;
+ case CommandTraits::KCI_helps:
+ DiagSelect = 6;
+ break;
+ case CommandTraits::KCI_instancesize:
+ DiagSelect = 7;
+ break;
+ case CommandTraits::KCI_ownership:
+ DiagSelect = 8;
+ break;
+ case CommandTraits::KCI_performance:
+ DiagSelect = 9;
+ break;
+ case CommandTraits::KCI_security:
+ DiagSelect = 10;
+ break;
+ case CommandTraits::KCI_superclass:
+ DiagSelect = 11;
+ break;
+ default:
+ DiagSelect = 0;
+ break;
+ }
+ if (DiagSelect)
+ Diag(Comment->getLocation(), diag::warn_doc_container_decl_mismatch)
+ << Comment->getCommandMarker()
+ << (DiagSelect-1)
+ << Comment->getSourceRange();
+}
+
void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,
SourceLocation ArgLocBegin,
SourceLocation ArgLocEnd,
@@ -158,15 +285,19 @@ void Sema::actOnParamCommandFinish(ParamCommandComment *Command,
checkBlockCommandEmptyParagraph(Command);
}
-TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin,
- SourceLocation LocEnd,
- unsigned CommandID) {
+TParamCommandComment *Sema::actOnTParamCommandStart(
+ SourceLocation LocBegin,
+ SourceLocation LocEnd,
+ unsigned CommandID,
+ CommandMarkerKind CommandMarker) {
TParamCommandComment *Command =
- new (Allocator) TParamCommandComment(LocBegin, LocEnd, CommandID);
+ new (Allocator) TParamCommandComment(LocBegin, LocEnd, CommandID,
+ CommandMarker);
if (!isTemplateOrSpecialization())
Diag(Command->getLocation(),
diag::warn_doc_tparam_not_attached_to_a_template_decl)
+ << CommandMarker
<< Command->getCommandNameRange(Traits);
return Command;
@@ -324,12 +455,15 @@ VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
unsigned CommandID,
SourceLocation TextBegin,
StringRef Text) {
- return new (Allocator) VerbatimLineComment(
+ VerbatimLineComment *VL = new (Allocator) VerbatimLineComment(
LocBegin,
TextBegin.getLocWithOffset(Text.size()),
CommandID,
TextBegin,
Text);
+ checkFunctionDeclVerbatimLine(VL);
+ checkContainerDeclVerbatimLine(VL);
+ return VL;
}
HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
@@ -430,6 +564,7 @@ void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
if (!DiagLoc.isValid())
DiagLoc = Command->getCommandNameRange(Traits).getEnd();
Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
+ << Command->getCommandMarker()
<< Command->getCommandName(Traits)
<< Command->getSourceRange();
}
@@ -457,14 +592,19 @@ void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
}
Diag(Command->getLocation(),
diag::warn_doc_returns_attached_to_a_void_function)
+ << Command->getCommandMarker()
<< Command->getCommandName(Traits)
<< DiagKind
<< Command->getSourceRange();
}
return;
}
+ else if (isObjCPropertyDecl())
+ return;
+
Diag(Command->getLocation(),
diag::warn_doc_returns_not_attached_to_a_function_decl)
+ << Command->getCommandMarker()
<< Command->getCommandName(Traits)
<< Command->getSourceRange();
}
@@ -484,6 +624,12 @@ void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
return;
}
PrevCommand = ReturnsCommand;
+ } else if (Info->IsHeaderfileCommand) {
+ if (!HeaderfileCommand) {
+ HeaderfileCommand = Command;
+ return;
+ }
+ PrevCommand = HeaderfileCommand;
} else {
// We don't want to check this command for duplicates.
return;
@@ -491,15 +637,18 @@ void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
StringRef CommandName = Command->getCommandName(Traits);
StringRef PrevCommandName = PrevCommand->getCommandName(Traits);
Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)
+ << Command->getCommandMarker()
<< CommandName
<< Command->getSourceRange();
if (CommandName == PrevCommandName)
Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
+ << PrevCommand->getCommandMarker()
<< PrevCommandName
<< PrevCommand->getSourceRange();
else
Diag(PrevCommand->getLocation(),
diag::note_doc_block_command_previous_alias)
+ << PrevCommand->getCommandMarker()
<< PrevCommandName
<< CommandName;
}
@@ -559,11 +708,11 @@ void Sema::resolveParamCommandIndexes(const FullComment *FC) {
return;
}
- llvm::SmallVector<ParamCommandComment *, 8> UnresolvedParamCommands;
+ SmallVector<ParamCommandComment *, 8> UnresolvedParamCommands;
// Comment AST nodes that correspond to \c ParamVars for which we have
// found a \\param command or NULL if no documentation was found so far.
- llvm::SmallVector<ParamCommandComment *, 8> ParamVarDocs;
+ SmallVector<ParamCommandComment *, 8> ParamVarDocs;
ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
ParamVarDocs.resize(ParamVars.size(), NULL);
@@ -596,7 +745,7 @@ void Sema::resolveParamCommandIndexes(const FullComment *FC) {
}
// Find parameter declarations that have no corresponding \\param.
- llvm::SmallVector<const ParmVarDecl *, 8> OrphanedParamDecls;
+ SmallVector<const ParmVarDecl *, 8> OrphanedParamDecls;
for (unsigned i = 0, e = ParamVarDocs.size(); i != e; ++i) {
if (!ParamVarDocs[i])
OrphanedParamDecls.push_back(ParamVars[i]);
@@ -645,6 +794,40 @@ bool Sema::isFunctionDecl() {
return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
}
+bool Sema::isAnyFunctionDecl() {
+ return isFunctionDecl() && ThisDeclInfo->CurrentDecl &&
+ isa<FunctionDecl>(ThisDeclInfo->CurrentDecl);
+}
+
+bool Sema::isObjCMethodDecl() {
+ return isFunctionDecl() && ThisDeclInfo->CurrentDecl &&
+ isa<ObjCMethodDecl>(ThisDeclInfo->CurrentDecl);
+}
+
+/// isFunctionPointerVarDecl - returns 'true' if declaration is a pointer to
+/// function decl.
+bool Sema::isFunctionPointerVarDecl() {
+ if (!ThisDeclInfo)
+ return false;
+ if (!ThisDeclInfo->IsFilled)
+ inspectThisDecl();
+ if (ThisDeclInfo->getKind() == DeclInfo::VariableKind) {
+ if (const VarDecl *VD = dyn_cast_or_null<VarDecl>(ThisDeclInfo->CurrentDecl)) {
+ QualType QT = VD->getType();
+ return QT->isFunctionPointerType();
+ }
+ }
+ return false;
+}
+
+bool Sema::isObjCPropertyDecl() {
+ if (!ThisDeclInfo)
+ return false;
+ if (!ThisDeclInfo->IsFilled)
+ inspectThisDecl();
+ return ThisDeclInfo->CurrentDecl->getKind() == Decl::ObjCProperty;
+}
+
bool Sema::isTemplateOrSpecialization() {
if (!ThisDeclInfo)
return false;
@@ -653,6 +836,54 @@ bool Sema::isTemplateOrSpecialization() {
return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
}
+bool Sema::isRecordLikeDecl() {
+ if (!ThisDeclInfo)
+ return false;
+ if (!ThisDeclInfo->IsFilled)
+ inspectThisDecl();
+ return isUnionDecl() || isClassOrStructDecl()
+ || isObjCInterfaceDecl() || isObjCProtocolDecl();
+}
+
+bool Sema::isUnionDecl() {
+ if (!ThisDeclInfo)
+ return false;
+ if (!ThisDeclInfo->IsFilled)
+ inspectThisDecl();
+ if (const RecordDecl *RD =
+ dyn_cast_or_null<RecordDecl>(ThisDeclInfo->CurrentDecl))
+ return RD->isUnion();
+ return false;
+}
+
+bool Sema::isClassOrStructDecl() {
+ if (!ThisDeclInfo)
+ return false;
+ if (!ThisDeclInfo->IsFilled)
+ inspectThisDecl();
+ return ThisDeclInfo->CurrentDecl &&
+ isa<RecordDecl>(ThisDeclInfo->CurrentDecl) &&
+ !isUnionDecl();
+}
+
+bool Sema::isObjCInterfaceDecl() {
+ if (!ThisDeclInfo)
+ return false;
+ if (!ThisDeclInfo->IsFilled)
+ inspectThisDecl();
+ return ThisDeclInfo->CurrentDecl &&
+ isa<ObjCInterfaceDecl>(ThisDeclInfo->CurrentDecl);
+}
+
+bool Sema::isObjCProtocolDecl() {
+ if (!ThisDeclInfo)
+ return false;
+ if (!ThisDeclInfo->IsFilled)
+ inspectThisDecl();
+ return ThisDeclInfo->CurrentDecl &&
+ isa<ObjCProtocolDecl>(ThisDeclInfo->CurrentDecl);
+}
+
ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
if (!ThisDeclInfo->IsFilled)
inspectThisDecl();
OpenPOWER on IntegriCloud