From 0bd9838751384181ff387f2fb346896792b89617 Mon Sep 17 00:00:00 2001 From: Dmitri Gribenko Date: Sat, 22 Sep 2012 21:47:50 +0000 Subject: [PATCH] Comment sema: warn when comment has \deprecated but declaration does not have a deprecation attribute ('deprecated', 'availability' or 'unavailable'). This warning is under a separate flag, -Wdocumentation-deprecated-sync, so it can be turned off easily while leaving other -Wdocumentation warnings on. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@164467 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/CommentCommandTraits.h | 3 ++ include/clang/AST/CommentCommands.td | 6 ++- include/clang/AST/CommentSema.h | 2 + include/clang/Basic/DiagnosticCommentKinds.td | 10 +++++ include/clang/Basic/DiagnosticGroups.td | 7 +++- lib/AST/CommentSema.cpp | 34 +++++++++++++++++ test/Sema/warn-documentation-fixits.cpp | 37 +++++++++++++++++++ test/Sema/warn-documentation.cpp | 29 ++++++++++++++- .../ClangCommentCommandInfoEmitter.cpp | 1 + 9 files changed, 125 insertions(+), 4 deletions(-) diff --git a/include/clang/AST/CommentCommandTraits.h b/include/clang/AST/CommentCommandTraits.h index 73ded54a31..6d44c706c3 100644 --- a/include/clang/AST/CommentCommandTraits.h +++ b/include/clang/AST/CommentCommandTraits.h @@ -66,6 +66,9 @@ struct CommandInfo { /// a template parameter (\\tparam or an alias). unsigned IsTParamCommand : 1; + /// True if this command is \\deprecated or an alias. + unsigned IsDeprecatedCommand : 1; + /// True if we don't want to warn about this command being passed an empty /// paragraph. Meaningful only for block commands. unsigned IsEmptyParagraphAllowed : 1; diff --git a/include/clang/AST/CommentCommands.td b/include/clang/AST/CommentCommands.td index ced9e9ef7f..3d8bad89c2 100644 --- a/include/clang/AST/CommentCommands.td +++ b/include/clang/AST/CommentCommands.td @@ -11,6 +11,7 @@ class Command { bit IsReturnsCommand = 0; bit IsParamCommand = 0; bit IsTParamCommand = 0; + bit IsDeprecatedCommand = 0; bit IsEmptyParagraphAllowed = 0; @@ -75,7 +76,10 @@ def Tparam : BlockCommand<"tparam"> { let IsTParamCommand = 1; } // HeaderDoc def Templatefield : BlockCommand<"templatefield"> { let IsTParamCommand = 1; } -def Deprecated : BlockCommand<"deprecated"> { let IsEmptyParagraphAllowed = 1; } +def Deprecated : BlockCommand<"deprecated"> { + let IsEmptyParagraphAllowed = 1; + let IsDeprecatedCommand = 1; +} def Author : BlockCommand<"author">; def Authors : BlockCommand<"authors">; diff --git a/include/clang/AST/CommentSema.h b/include/clang/AST/CommentSema.h index c913d28dae..2c4efc307c 100644 --- a/include/clang/AST/CommentSema.h +++ b/include/clang/AST/CommentSema.h @@ -187,6 +187,8 @@ public: /// used only once per comment, e.g., \\brief and \\returns. void checkBlockCommandDuplicate(const BlockCommandComment *Command); + void checkDeprecatedCommand(const BlockCommandComment *Comment); + /// Resolve parameter names to parameter indexes in function declaration. /// Emit diagnostics about unknown parametrs. void resolveParamCommandIndexes(const FullComment *FC); diff --git a/include/clang/Basic/DiagnosticCommentKinds.td b/include/clang/Basic/DiagnosticCommentKinds.td index 235ca79564..7203ac75f8 100644 --- a/include/clang/Basic/DiagnosticCommentKinds.td +++ b/include/clang/Basic/DiagnosticCommentKinds.td @@ -121,5 +121,15 @@ def warn_doc_returns_attached_to_a_void_function : Warning< "method returning void}1">, InGroup, DefaultIgnore; +// \deprecated command + +def warn_doc_deprecated_not_sync : Warning< + "declaration is marked with '\\deprecated' command but does not have " + "a deprecation attribute">, + InGroup, DefaultIgnore; + +def note_add_deprecation_attr : Note< + "add a deprecation attribute to the declaration to silence this warning">; + } // end of documentation issue category } // end of AST component diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 223c6c5e29..a4e9231daa 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -61,9 +61,14 @@ def DeprecatedImplementations :DiagGroup<"deprecated-implementations">; def : DiagGroup<"disabled-optimization">; def : DiagGroup<"discard-qual">; def : DiagGroup<"div-by-zero">; + def DocumentationHTML : DiagGroup<"documentation-html">; def DocumentationPedantic : DiagGroup<"documentation-pedantic">; -def Documentation : DiagGroup<"documentation", [DocumentationHTML]>; +def DocumentationDeprecatedSync : DiagGroup<"documentation-deprecated-sync">; +def Documentation : DiagGroup<"documentation", + [DocumentationHTML, + DocumentationDeprecatedSync]>; + def EmptyBody : DiagGroup<"empty-body">; def ExtraTokens : DiagGroup<"extra-tokens">; def CXX11ExtraSemi : DiagGroup<"c++11-extra-semi">; diff --git a/lib/AST/CommentSema.cpp b/lib/AST/CommentSema.cpp index 6d84a2a4b9..4f9f1f241c 100644 --- a/lib/AST/CommentSema.cpp +++ b/lib/AST/CommentSema.cpp @@ -59,6 +59,7 @@ void Sema::actOnBlockCommandFinish(BlockCommandComment *Command, checkBlockCommandEmptyParagraph(Command); checkBlockCommandDuplicate(Command); checkReturnsCommand(Command); + checkDeprecatedCommand(Command); } ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin, @@ -500,6 +501,39 @@ void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) { << CommandName; } +void Sema::checkDeprecatedCommand(const BlockCommandComment *Command) { + if (!Traits.getCommandInfo(Command->getCommandID())->IsDeprecatedCommand) + return; + + const Decl *D = ThisDeclInfo->ThisDecl; + if (!D) + return; + + if (D->hasAttr() || + D->hasAttr() || + D->hasAttr()) + return; + + Diag(Command->getLocation(), + diag::warn_doc_deprecated_not_sync) + << Command->getSourceRange(); + + // Try to emit a fixit with a deprecation attribute. + if (const FunctionDecl *FD = dyn_cast(D)) { + // Don't emit a Fix-It for non-member function definitions. GCC does not + // accept attributes on them. + const DeclContext *Ctx = FD->getDeclContext(); + if ((!Ctx || !Ctx->isRecord()) && + FD->doesThisDeclarationHaveABody()) + return; + + Diag(FD->getLocEnd(), + diag::note_add_deprecation_attr) + << FixItHint::CreateInsertion(FD->getLocEnd().getLocWithOffset(1), + " __attribute__((deprecated))"); + } +} + void Sema::resolveParamCommandIndexes(const FullComment *FC) { if (!isFunctionDecl()) { // We already warned that \\param commands are not attached to a function diff --git a/test/Sema/warn-documentation-fixits.cpp b/test/Sema/warn-documentation-fixits.cpp index 732b44db02..812d404e68 100644 --- a/test/Sema/warn-documentation-fixits.cpp +++ b/test/Sema/warn-documentation-fixits.cpp @@ -20,8 +20,45 @@ void test3(T aaa); template void test4(SomeTy aaa, OtherTy bbb); +// expected-warning@+1 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} +/// \deprecated +void test_deprecated_1(); + +// expected-warning@+1 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} +/// \deprecated +void test_deprecated_2(int a); + +struct test_deprecated_3 { + // expected-warning@+1 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} + /// \deprecated + void test_deprecated_4(); + + // expected-warning@+1 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} + /// \deprecated + void test_deprecated_5() { + } +}; + +template +struct test_deprecated_6 { + // expected-warning@+1 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} + /// \deprecated + void test_deprecated_7(); + + // expected-warning@+1 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+2 {{add a deprecation attribute to the declaration to silence this warning}} + /// \deprecated + void test_deprecated_8() { + } +}; + // CHECK: fix-it:"{{.*}}":{5:12-5:22}:"a" // CHECK: fix-it:"{{.*}}":{9:12-9:15}:"aaa" // CHECK: fix-it:"{{.*}}":{13:13-13:23}:"T" // CHECK: fix-it:"{{.*}}":{18:13-18:18}:"SomeTy" +// CHECK: fix-it:"{{.*}}":{25:25-25:25}:" __attribute__((deprecated))" +// CHECK: fix-it:"{{.*}}":{29:30-29:30}:" __attribute__((deprecated))" +// CHECK: fix-it:"{{.*}}":{34:27-34:27}:" __attribute__((deprecated))" +// CHECK: fix-it:"{{.*}}":{38:27-38:27}:" __attribute__((deprecated))" +// CHECK: fix-it:"{{.*}}":{46:27-46:27}:" __attribute__((deprecated))" +// CHECK: fix-it:"{{.*}}":{50:27-50:27}:" __attribute__((deprecated))" diff --git a/test/Sema/warn-documentation.cpp b/test/Sema/warn-documentation.cpp index 649f072ab9..b5d3300efd 100644 --- a/test/Sema/warn-documentation.cpp +++ b/test/Sema/warn-documentation.cpp @@ -380,14 +380,39 @@ using test_tparam15 = test_tparam13; /// Aaa /// \deprecated Bbb -void test_deprecated_1(int a); +void test_deprecated_1(int a) __attribute__((deprecated)); // We don't want \deprecated to warn about empty paragraph. It is fine to use // \deprecated by itself without explanations. /// Aaa /// \deprecated -void test_deprecated_2(int a); +void test_deprecated_2(int a) __attribute__((deprecated)); + +/// Aaa +/// \deprecated +void test_deprecated_3(int a) __attribute__((availability(macosx,introduced=10.4))); + +/// Aaa +/// \deprecated +void test_deprecated_4(int a) __attribute__((unavailable)); + +// expected-warning@+2 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+3 {{add a deprecation attribute to the declaration to silence this warning}} +/// Aaa +/// \deprecated +void test_deprecated_5(int a); + +// expected-warning@+2 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} expected-note@+3 {{add a deprecation attribute to the declaration to silence this warning}} +/// Aaa +/// \deprecated +void test_deprecated_6(int a) { +} + +// expected-warning@+2 {{declaration is marked with '\deprecated' command but does not have a deprecation attribute}} +/// Aaa +/// \deprecated +template +void test_deprecated_7(T aaa); /// \invariant aaa diff --git a/utils/TableGen/ClangCommentCommandInfoEmitter.cpp b/utils/TableGen/ClangCommentCommandInfoEmitter.cpp index 599d1385a8..36fbcd40b2 100644 --- a/utils/TableGen/ClangCommentCommandInfoEmitter.cpp +++ b/utils/TableGen/ClangCommentCommandInfoEmitter.cpp @@ -38,6 +38,7 @@ void EmitClangCommentCommandInfo(RecordKeeper &Records, raw_ostream &OS) { << Tag.getValueAsBit("IsReturnsCommand") << ", " << Tag.getValueAsBit("IsParamCommand") << ", " << Tag.getValueAsBit("IsTParamCommand") << ", " + << Tag.getValueAsBit("IsDeprecatedCommand") << ", " << Tag.getValueAsBit("IsEmptyParagraphAllowed") << ", " << Tag.getValueAsBit("IsVerbatimBlockCommand") << ", " << Tag.getValueAsBit("IsVerbatimBlockEndCommand") << ", " -- 2.40.0