HeaderDoc: Support more of HeaderDoc documentation

commands; top level tags such as @interface and
their 2nd level tags such as @coclass, etc.
// rdar://12379114


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@176667 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Fariborz Jahanian 2013-03-07 23:33:11 +00:00
Родитель 0689863f7b
Коммит 28c1cd2138
8 изменённых файлов: 257 добавлений и 8 удалений

Просмотреть файл

@ -101,8 +101,15 @@ struct CommandInfo {
/// \endcode
unsigned IsDeclarationCommand : 1;
/// \brief True if verbatim-like line command is a function declaraton.
/// \brief True if verbatim-like line command is a function declaration.
unsigned IsFunctionDeclarationCommand : 1;
/// \brief True if block command is further describing a container API; such
/// as @coclass, @classdesign, etc.
unsigned IsContainerDetailCommand : 1;
/// \brief True if block command is a container API; such as @interface.
unsigned IsContainerDeclarationCommand : 1;
/// \brief True if this command is unknown. This \c CommandInfo object was
/// created during parsing.

Просмотреть файл

@ -25,6 +25,8 @@ class Command<string name> {
bit IsVerbatimLineCommand = 0;
bit IsDeclarationCommand = 0;
bit IsFunctionDeclarationCommand = 0;
bit IsContainerDetailCommand = 0;
bit IsContainerDeclarationCommand = 0;
}
class InlineCommand<string name> : Command<name> {
@ -66,6 +68,12 @@ class FunctionDeclarationVerbatimLineCommand<string name> :
let IsFunctionDeclarationCommand = 1;
}
class ContainerDeclarationVerbatimLineCommand<string name> :
VerbatimLineCommand<name> {
let IsDeclarationCommand = 1;
let IsContainerDeclarationCommand = 1;
}
//===----------------------------------------------------------------------===//
// InlineCommand
//===----------------------------------------------------------------------===//
@ -181,9 +189,11 @@ def Typedef : DeclarationVerbatimLineCommand<"typedef">;
def Var : DeclarationVerbatimLineCommand<"var">;
// HeaderDoc commands.
def Class : DeclarationVerbatimLineCommand<"class">;
def Interface : DeclarationVerbatimLineCommand<"interface">;
def Protocol : DeclarationVerbatimLineCommand<"protocol">;
def Class : ContainerDeclarationVerbatimLineCommand<"class">;
def Interface : ContainerDeclarationVerbatimLineCommand<"interface">;
def Protocol : ContainerDeclarationVerbatimLineCommand<"protocol">;
def Struct : ContainerDeclarationVerbatimLineCommand<"struct">;
def Union : ContainerDeclarationVerbatimLineCommand<"union">;
def Category : DeclarationVerbatimLineCommand<"category">;
def Template : DeclarationVerbatimLineCommand<"template">;
def Function : FunctionDeclarationVerbatimLineCommand<"function">;
@ -191,7 +201,38 @@ def Method : FunctionDeclarationVerbatimLineCommand<"method">;
def Callback : FunctionDeclarationVerbatimLineCommand<"callback">;
def Const : DeclarationVerbatimLineCommand<"const">;
def Constant : DeclarationVerbatimLineCommand<"constant">;
def Struct : DeclarationVerbatimLineCommand<"struct">;
def Union : DeclarationVerbatimLineCommand<"union">;
def Enum : DeclarationVerbatimLineCommand<"enum">;
def ClassDesign : BlockCommand<"classdesign"> {
let IsContainerDetailCommand = 1;
}
def CoClass : BlockCommand<"coclass"> {
let IsContainerDetailCommand = 1;
}
def Dependency : BlockCommand<"dependency"> {
let IsContainerDetailCommand = 1;
}
def Helper : BlockCommand<"helper"> {
let IsContainerDetailCommand = 1;
}
def HelperClass : BlockCommand<"helperclass"> {
let IsContainerDetailCommand = 1;
}
def Helps : BlockCommand<"helps"> {
let IsContainerDetailCommand = 1;
}
def InstanceSize : BlockCommand<"instancesize"> {
let IsContainerDetailCommand = 1;
}
def Ownership : BlockCommand<"ownership"> {
let IsContainerDetailCommand = 1;
}
def Performance : BlockCommand<"performance"> {
let IsContainerDetailCommand = 1;
}
def Security : BlockCommand<"security"> {
let IsContainerDetailCommand = 1;
}
def SuperClass : BlockCommand<"superclass"> {
let IsContainerDetailCommand = 1;
}

Просмотреть файл

@ -200,6 +200,10 @@ public:
void checkDeprecatedCommand(const BlockCommandComment *Comment);
void checkFunctionDeclVerbatimLine(const BlockCommandComment *Comment);
void checkContainerDeclVerbatimLine(const BlockCommandComment *Comment);
void checkContainerDecl(const BlockCommandComment *Comment);
/// Resolve parameter names to parameter indexes in function declaration.
/// Emit diagnostics about unknown parametrs.
@ -211,6 +215,11 @@ public:
bool isObjCMethodDecl();
bool isObjCPropertyDecl();
bool isTemplateOrSpecialization();
bool isContainerDecl();
bool isClassStructDecl();
bool isUnionDecl();
bool isObjCInterfaceDecl();
bool isObjCProtocolDecl();
ArrayRef<const ParmVarDecl *> getParamVars();

Просмотреть файл

@ -79,6 +79,18 @@ def warn_doc_function_method_decl_mismatch : Warning<
"%select{a function|an Objective-C method|a pointer to function}2 declaration">,
InGroup<Documentation>, DefaultIgnore;
def warn_doc_api_container_decl_mismatch : Warning<
"'%select{\\|@}0%select{class|interface|protocol|struct|union}1' "
"command should not be used in a comment attached to a "
"non-%select{class|interface|protocol|struct|union}2 declaration">,
InGroup<Documentation>, DefaultIgnore;
def warn_doc_container_decl_mismatch : Warning<
"'%select{\\|@}0%select{classdesign|coclass|dependency|helper"
"|helperclass|helps|instancesize|ownership|performance|security|superclass}1' "
"command should not be used in a comment attached to a non-container declaration">,
InGroup<Documentation>, DefaultIgnore;
def warn_doc_param_duplicate : Warning<
"parameter '%0' is already documented">,
InGroup<Documentation>, DefaultIgnore;

Просмотреть файл

@ -52,8 +52,11 @@ BlockCommandComment *Sema::actOnBlockCommandStart(
SourceLocation LocEnd,
unsigned CommandID,
CommandMarkerKind CommandMarker) {
return new (Allocator) BlockCommandComment(LocBegin, LocEnd, CommandID,
CommandMarker);
BlockCommandComment *BC = new (Allocator) BlockCommandComment(LocBegin, LocEnd,
CommandID,
CommandMarker);
checkContainerDecl(BC);
return BC;
}
void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
@ -105,6 +108,52 @@ void Sema::checkFunctionDeclVerbatimLine(const BlockCommandComment *Comment) {
<< (DiagSelect-1) << (DiagSelect-1)
<< Comment->getSourceRange();
}
void Sema::checkContainerDeclVerbatimLine(const BlockCommandComment *Comment) {
const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
if (!Info->IsContainerDeclarationCommand)
return;
StringRef Name = Info->Name;
unsigned DiagSelect = llvm::StringSwitch<unsigned>(Name)
.Case("class", !isClassStructDecl() ? 1 : 0)
.Case("interface", !isObjCInterfaceDecl() ? 2 : 0)
.Case("protocol", !isObjCProtocolDecl() ? 3 : 0)
.Case("struct", !isClassStructDecl() ? 4 : 0)
.Case("union", !isUnionDecl() ? 5 : 0)
.Default(0);
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->IsContainerDetailCommand || isContainerDecl())
return;
StringRef Name = Info->Name;
unsigned DiagSelect = llvm::StringSwitch<unsigned>(Name)
.Case("classdesign", 1)
.Case("coclass", 2)
.Case("dependency", 3)
.Case("helper", 4)
.Case("helperclass", 5)
.Case("helps", 6)
.Case("instancesize", 7)
.Case("ownership", 8)
.Case("performance", 9)
.Case("security", 10)
.Case("superclass", 11)
.Default(0);
if (DiagSelect)
Diag(Comment->getLocation(), diag::warn_doc_container_decl_mismatch)
<< Comment->getCommandMarker()
<< (DiagSelect-1)
<< Comment->getSourceRange();
}
void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,
SourceLocation ArgLocBegin,
@ -362,6 +411,7 @@ VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
TextBegin,
Text);
checkFunctionDeclVerbatimLine(VL);
checkContainerDeclVerbatimLine(VL);
return VL;
}
@ -735,6 +785,54 @@ bool Sema::isTemplateOrSpecialization() {
return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
}
bool Sema::isContainerDecl() {
if (!ThisDeclInfo)
return false;
if (!ThisDeclInfo->IsFilled)
inspectThisDecl();
return isUnionDecl() || isClassStructDecl()
|| 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::isClassStructDecl() {
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();

Просмотреть файл

@ -922,3 +922,31 @@ int test_nocrash12();
// expected-warning@+1 {{empty paragraph passed to '@param' command}}
///@param x@param y
int test_nocrash13(int x, int y);
// rdar://12379114
// expected-warning@+2 {{'@union' command should not be used in a comment attached to a non-union declaration}}
/*!
@union U This is new
*/
struct U { int iS; };
/*!
@union U1
*/
union U1 {int i; };
// expected-warning@+2 {{'@struct' command should not be used in a comment attached to a non-struct declaration}}
/*!
@struct S2
*/
union S2 {};
/*!
@class C1
*/
class C1;
/*!
@struct S3;
*/
class S3;

Просмотреть файл

@ -105,3 +105,55 @@ typedef int (^test_param1)(int aaa, int ccc);
typedef id ID;
- (unsigned) Base64EncodeEx : (ID)Arg;
@end
// rdar://12379114
// expected-warning@+5 {{'@interface' command should not be used in a comment attached to a non-interface declaration}}
// expected-warning@+5 {{'@classdesign' command should not be used in a comment attached to a non-container declaration}}
// expected-warning@+5 {{'@coclass' command should not be used in a comment attached to a non-container declaration}}
@interface NSObject @end
/*!
@interface IOCommandGate
@classdesign Multiple paragraphs go here.
@coclass myCoClass
*/
typedef id OBJ;
@interface IOCommandGate : NSObject {
OBJ iv;
}
@end
// expected-warning@+2 {{'@protocol' command should not be used in a comment attached to a non-protocol declaration}}
/*!
@protocol PROTO
*/
struct S;
/*!
@interface NSArray This is an array
*/
@class NSArray;
@interface NSArray @end
/*!
@interface NSMutableArray
@super NSArray
*/
@interface NSMutableArray : NSArray @end
/*!
@protocol MyProto
*/
@protocol MyProto @end
// expected-warning@+2 {{'@protocol' command should not be used in a comment attached to a non-protocol declaration}}
/*!
@protocol MyProto
*/
@interface INTF <MyProto> @end
// expected-warning@+2 {{'@struct' command should not be used in a comment attached to a non-struct declaration}}
/*!
@struct S1 THIS IS IT
*/
@interface S1 @end

Просмотреть файл

@ -48,6 +48,8 @@ void EmitClangCommentCommandInfo(RecordKeeper &Records, raw_ostream &OS) {
<< Tag.getValueAsBit("IsVerbatimLineCommand") << ", "
<< Tag.getValueAsBit("IsDeclarationCommand") << ", "
<< Tag.getValueAsBit("IsFunctionDeclarationCommand") << ", "
<< Tag.getValueAsBit("IsContainerDetailCommand") << ", "
<< Tag.getValueAsBit("IsContainerDeclarationCommand") << ", "
<< /* IsUnknownCommand = */ "0"
<< " }";
if (i + 1 != e)