Comment sema: warn when comment has \deprecated but declaration does not have a
authorDmitri Gribenko <gribozavr@gmail.com>
Sat, 22 Sep 2012 21:47:50 +0000 (21:47 +0000)
committerDmitri Gribenko <gribozavr@gmail.com>
Sat, 22 Sep 2012 21:47:50 +0000 (21:47 +0000)
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.

llvm-svn: 164467

clang/include/clang/AST/CommentCommandTraits.h
clang/include/clang/AST/CommentCommands.td
clang/include/clang/AST/CommentSema.h
clang/include/clang/Basic/DiagnosticCommentKinds.td
clang/include/clang/Basic/DiagnosticGroups.td
clang/lib/AST/CommentSema.cpp
clang/test/Sema/warn-documentation-fixits.cpp
clang/test/Sema/warn-documentation.cpp
clang/utils/TableGen/ClangCommentCommandInfoEmitter.cpp

index 73ded54..6d44c70 100644 (file)
@@ -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;
index ced9e9e..3d8bad8 100644 (file)
@@ -11,6 +11,7 @@ class Command<string name> {
   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">;
index c913d28..2c4efc3 100644 (file)
@@ -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);
index 235ca79..7203ac7 100644 (file)
@@ -121,5 +121,15 @@ def warn_doc_returns_attached_to_a_void_function : Warning<
   "method returning void}1">,
   InGroup<Documentation>, DefaultIgnore;
 
+// \deprecated command
+
+def warn_doc_deprecated_not_sync : Warning<
+  "declaration is marked with '\\deprecated' command but does not have "
+  "a deprecation attribute">,
+  InGroup<DocumentationDeprecatedSync>, 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
index 223c6c5..a4e9231 100644 (file)
@@ -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">;
index 6d84a2a..4f9f1f2 100644 (file)
@@ -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<DeprecatedAttr>() ||
+      D->hasAttr<AvailabilityAttr>() ||
+      D->hasAttr<UnavailableAttr>())
+    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<FunctionDecl>(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
index 732b44d..812d404 100644 (file)
@@ -20,8 +20,45 @@ void test3(T aaa);
 template<typename SomeTy, typename OtherTy>
 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<typename T>
+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))"
 
index 649f072..b5d3300 100644 (file)
@@ -380,14 +380,39 @@ using test_tparam15 = test_tparam13<T, int>;
 
 /// 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<typename T>
+void test_deprecated_7(T aaa);
 
 
 /// \invariant aaa
index 599d138..36fbcd4 100644 (file)
@@ -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") << ", "