diff --git a/include/clang/AST/CommentCommandTraits.h b/include/clang/AST/CommentCommandTraits.h index 360a54b3781fc97c469ae97dd188cce964748cbb..0dfeea0cb99f2234e5859269335ec74f3ee0e1cb 100644 --- a/include/clang/AST/CommentCommandTraits.h +++ b/include/clang/AST/CommentCommandTraits.h @@ -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. diff --git a/include/clang/AST/CommentCommands.td b/include/clang/AST/CommentCommands.td index f3d9baab437c2cb444e4ab854df5a4aab170784e..ed5927cc0dc2f5dc54334b5bfd3f545cb46c9cef 100644 --- a/include/clang/AST/CommentCommands.td +++ b/include/clang/AST/CommentCommands.td @@ -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; +} diff --git a/include/clang/AST/CommentSema.h b/include/clang/AST/CommentSema.h index 1d8112fa566a7a069d77ef2ab9b29a4161c483da..8294a816c05d25121cc1d35d0613d311631d3d97 100644 --- a/include/clang/AST/CommentSema.h +++ b/include/clang/AST/CommentSema.h @@ -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(); diff --git a/include/clang/Basic/DiagnosticCommentKinds.td b/include/clang/Basic/DiagnosticCommentKinds.td index c87a0a72ef7ba85f9d468c93df007e56b723408e..7682b85a137a6319de91ec2b76d2ace61d798c35 100644 --- a/include/clang/Basic/DiagnosticCommentKinds.td +++ b/include/clang/Basic/DiagnosticCommentKinds.td @@ -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; diff --git a/lib/AST/CommentSema.cpp b/lib/AST/CommentSema.cpp index 68a9ebbd5a89450cf43f9a08b9ce70bce22eace0..0ca6fb1a117017bddbea1e43faa2485caef0a727 100644 --- a/lib/AST/CommentSema.cpp +++ b/lib/AST/CommentSema.cpp @@ -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(); diff --git a/test/Sema/warn-documentation.cpp b/test/Sema/warn-documentation.cpp index 32e43a77f98363234f14e0c4f179056c81d328d7..0132ef280c47ac45dee9cb9aba54257147faaf87 100644 --- a/test/Sema/warn-documentation.cpp +++ b/test/Sema/warn-documentation.cpp @@ -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; diff --git a/test/Sema/warn-documentation.m b/test/Sema/warn-documentation.m index 5150955f63cc14b5e83d8aa7c1c6f7c0dd7cdc9c..98c499356183ab8a6fec5d2b0010f907e72102c0 100644 --- a/test/Sema/warn-documentation.m +++ b/test/Sema/warn-documentation.m @@ -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 diff --git a/utils/TableGen/ClangCommentCommandInfoEmitter.cpp b/utils/TableGen/ClangCommentCommandInfoEmitter.cpp index b0bf75217dd43f6776334f320e5ff567aeac6736..f90cef3719645a3cdee6c61557b1a9163f615642 100644 --- a/utils/TableGen/ClangCommentCommandInfoEmitter.cpp +++ b/utils/TableGen/ClangCommentCommandInfoEmitter.cpp @@ -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)