Move the 'find macro by spelling' infrastructure to the Preprocessor class and
authorDmitri Gribenko <gribozavr@gmail.com>
Sat, 29 Sep 2012 11:40:46 +0000 (11:40 +0000)
committerDmitri Gribenko <gribozavr@gmail.com>
Sat, 29 Sep 2012 11:40:46 +0000 (11:40 +0000)
use it to suggest appropriate macro for __attribute__((deprecated)) in
-Wdocumentation-deprecated-sync.

llvm-svn: 164892

13 files changed:
clang/include/clang/AST/ASTContext.h
clang/include/clang/AST/CommentSema.h
clang/include/clang/AST/RawCommentList.h
clang/include/clang/Lex/Preprocessor.h
clang/lib/AST/ASTContext.cpp
clang/lib/AST/CommentSema.cpp
clang/lib/AST/RawCommentList.cpp
clang/lib/Lex/Preprocessor.cpp
clang/lib/Sema/AnalysisBasedWarnings.cpp
clang/lib/Sema/SemaDecl.cpp
clang/test/Sema/warn-documentation-fixits.cpp
clang/tools/libclang/CIndex.cpp
clang/unittests/AST/CommentParser.cpp

index 1d037ac..f52ee04 100644 (file)
@@ -532,7 +532,11 @@ public:
 
   /// Return parsed documentation comment attached to a given declaration.
   /// Returns NULL if no comment is attached.
-  comments::FullComment *getCommentForDecl(const Decl *D) const;
+  ///
+  /// \param PP the Preprocessor used with this TU.  Could be NULL if
+  /// preprocessor is not available.
+  comments::FullComment *getCommentForDecl(const Decl *D,
+                                           const Preprocessor *PP) const;
 
 private:
   mutable comments::CommandTraits CommentCommandTraits;
index 2c4efc3..0340b3c 100644 (file)
@@ -25,6 +25,7 @@
 namespace clang {
 class Decl;
 class SourceMgr;
+class Preprocessor;
 
 namespace comments {
 class CommandTraits;
@@ -43,6 +44,8 @@ class Sema {
 
   CommandTraits &Traits;
 
+  const Preprocessor *PP;
+
   /// Information about the declaration this comment is attached to.
   DeclInfo *ThisDeclInfo;
 
@@ -68,7 +71,8 @@ class Sema {
 
 public:
   Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
-       DiagnosticsEngine &Diags, CommandTraits &Traits);
+       DiagnosticsEngine &Diags, CommandTraits &Traits,
+       const Preprocessor *PP);
 
   void setDecl(const Decl *D);
 
index 1778cca..3a8b218 100644 (file)
@@ -18,6 +18,7 @@ namespace clang {
 class ASTContext;
 class ASTReader;
 class Decl;
+class Preprocessor;
 
 namespace comments {
   class FullComment;
@@ -114,7 +115,8 @@ public:
   }
 
   /// Parse the comment, assuming it is attached to decl \c D.
-  comments::FullComment *parse(const ASTContext &Context, const Decl *D) const;
+  comments::FullComment *parse(const ASTContext &Context,
+                               const Preprocessor *PP, const Decl *D) const;
 
 private:
   SourceRange Range;
index c957906..03ff027 100644 (file)
@@ -55,6 +55,27 @@ class DirectoryLookup;
 class PreprocessingRecord;
 class ModuleLoader;
 
+/// \brief Stores token information for comparing actual tokens with
+/// predefined values.  Only handles simple tokens and identifiers.
+class TokenValue {
+  tok::TokenKind Kind;
+  IdentifierInfo *II;
+
+public:
+  TokenValue(tok::TokenKind Kind) : Kind(Kind), II(0) {
+    assert(Kind != tok::raw_identifier && "Raw identifiers are not supported.");
+    assert(Kind != tok::identifier &&
+           "Identifiers should be created by TokenValue(IdentifierInfo *)");
+    assert(!tok::isLiteral(Kind) && "Literals are not supported.");
+    assert(!tok::isAnnotation(Kind) && "Annotations are not supported.");
+  }
+  TokenValue(IdentifierInfo *II) : Kind(tok::identifier), II(II) {}
+  bool operator==(const Token &Tok) const {
+    return Tok.getKind() == Kind &&
+        (!II || II == Tok.getIdentifierInfo());
+  }
+};
+
 /// Preprocessor - This object engages in a tight little dance with the lexer to
 /// efficiently preprocess tokens.  Lexers know only about tokens within a
 /// single source file, and don't know anything about preprocessor-level issues
@@ -491,6 +512,12 @@ public:
   macro_iterator macro_begin(bool IncludeExternalMacros = true) const;
   macro_iterator macro_end(bool IncludeExternalMacros = true) const;
 
+  /// \brief Return the name of the macro defined before \p Loc that has
+  /// spelling \p Tokens.  If there are multiple macros with same spelling,
+  /// return the last one defined.
+  StringRef getLastMacroWithSpelling(SourceLocation Loc,
+                                     ArrayRef<TokenValue> Tokens) const;
+
   const std::string &getPredefines() const { return Predefines; }
   /// setPredefines - Set the predefines for this Preprocessor.  These
   /// predefines are automatically injected when parsing the main file.
index f19a2aa..83f3f9c 100644 (file)
@@ -355,7 +355,9 @@ const RawComment *ASTContext::getRawCommentForAnyRedecl(
   return RC;
 }
 
-comments::FullComment *ASTContext::getCommentForDecl(const Decl *D) const {
+comments::FullComment *ASTContext::getCommentForDecl(
+                                              const Decl *D,
+                                              const Preprocessor *PP) const {
   D = adjustDeclToTemplate(D);
   const Decl *Canonical = D->getCanonicalDecl();
   llvm::DenseMap<const Decl *, comments::FullComment *>::iterator Pos =
@@ -373,9 +375,9 @@ comments::FullComment *ASTContext::getCommentForDecl(const Decl *D) const {
   // because comments can contain references to parameter names which can be
   // different across redeclarations.
   if (D != OriginalDecl)
-    return getCommentForDecl(OriginalDecl);
+    return getCommentForDecl(OriginalDecl, PP);
 
-  comments::FullComment *FC = RC->parse(*this, D);
+  comments::FullComment *FC = RC->parse(*this, PP, D);
   ParsedComments[Canonical] = FC;
   return FC;
 }
index 4f9f1f2..dd746ad 100644 (file)
@@ -13,7 +13,9 @@
 #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"
 
 namespace clang {
 namespace comments {
@@ -23,9 +25,10 @@ namespace {
 } // unnamed namespace
 
 Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
-           DiagnosticsEngine &Diags, CommandTraits &Traits) :
+           DiagnosticsEngine &Diags, CommandTraits &Traits,
+           const Preprocessor *PP) :
     Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits),
-    ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) {
+    PP(PP), ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) {
 }
 
 void Sema::setDecl(const Decl *D) {
@@ -527,10 +530,25 @@ void Sema::checkDeprecatedCommand(const BlockCommandComment *Command) {
         FD->doesThisDeclarationHaveABody())
       return;
 
+    StringRef AttributeSpelling = "__attribute__((deprecated))";
+    if (PP) {
+      TokenValue Tokens[] = {
+        tok::kw___attribute, tok::l_paren, tok::l_paren,
+        PP->getIdentifierInfo("deprecated"),
+        tok::r_paren, tok::r_paren
+      };
+      StringRef MacroName = PP->getLastMacroWithSpelling(FD->getLocation(),
+                                                         Tokens);
+      if (!MacroName.empty())
+        AttributeSpelling = MacroName;
+    }
+
+    SmallString<64> TextToInsert(" ");
+    TextToInsert += AttributeSpelling;
     Diag(FD->getLocEnd(),
          diag::note_add_deprecation_attr)
       << FixItHint::CreateInsertion(FD->getLocEnd().getLocWithOffset(1),
-                                    " __attribute__((deprecated))");
+                                    TextToInsert);
   }
 }
 
index 28ef611..80b6272 100644 (file)
@@ -159,6 +159,7 @@ const char *RawComment::extractBriefText(const ASTContext &Context) const {
 }
 
 comments::FullComment *RawComment::parse(const ASTContext &Context,
+                                         const Preprocessor *PP,
                                          const Decl *D) const {
   // Make sure that RawText is valid.
   getRawText(Context.getSourceManager());
@@ -168,7 +169,8 @@ comments::FullComment *RawComment::parse(const ASTContext &Context,
                     RawText.begin(), RawText.end());
   comments::Sema S(Context.getAllocator(), Context.getSourceManager(),
                    Context.getDiagnostics(),
-                   Context.getCommentCommandTraits());
+                   Context.getCommentCommandTraits(),
+                   PP);
   S.setDecl(D);
   comments::Parser P(L, S, Context.getAllocator(), Context.getSourceManager(),
                      Context.getDiagnostics(),
index 872cda3..a0fc265 100644 (file)
@@ -285,6 +285,39 @@ Preprocessor::macro_end(bool IncludeExternalMacros) const {
   return Macros.end();
 }
 
+/// \brief Compares macro tokens with a specified token value sequence.
+static bool MacroDefinitionEquals(const MacroInfo *MI,
+                                  llvm::ArrayRef<TokenValue> Tokens) {
+  return Tokens.size() == MI->getNumTokens() &&
+      std::equal(Tokens.begin(), Tokens.end(), MI->tokens_begin());
+}
+
+StringRef Preprocessor::getLastMacroWithSpelling(
+                                    SourceLocation Loc,
+                                    ArrayRef<TokenValue> Tokens) const {
+  SourceLocation BestLocation;
+  StringRef BestSpelling;
+  for (Preprocessor::macro_iterator I = macro_begin(), E = macro_end();
+       I != E; ++I) {
+    if (!I->second->isObjectLike())
+      continue;
+    const MacroInfo *MI = I->second->findDefinitionAtLoc(Loc, SourceMgr);
+    if (!MI)
+      continue;
+    if (!MacroDefinitionEquals(MI, Tokens))
+      continue;
+    SourceLocation Location = I->second->getDefinitionLoc();
+    // Choose the macro defined latest.
+    if (BestLocation.isInvalid() ||
+        (Location.isValid() &&
+         SourceMgr.isBeforeInTranslationUnit(BestLocation, Location))) {
+      BestLocation = Location;
+      BestSpelling = I->first->getName();
+    }
+  }
+  return BestSpelling;
+}
+
 void Preprocessor::recomputeCurLexerKind() {
   if (CurLexer)
     CurLexerKind = CLK_Lexer;
index ad5c739..67d290b 100644 (file)
@@ -41,6 +41,7 @@
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/ImmutableMap.h"
 #include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Casting.h"
@@ -679,61 +680,6 @@ static bool DiagnoseUninitializedUse(Sema &S, const VarDecl *VD,
   return true;
 }
 
-/// \brief Stores token information for comparing actual tokens with
-/// predefined values. Only handles simple tokens and identifiers.
-class TokenValue {
-  tok::TokenKind Kind;
-  IdentifierInfo *II;
-
-public:
-  TokenValue(tok::TokenKind Kind) : Kind(Kind), II(0) {
-    assert(Kind != tok::raw_identifier && "Raw identifiers are not supported.");
-    assert(Kind != tok::identifier &&
-           "Identifiers should be created by TokenValue(IdentifierInfo *)");
-    assert(!tok::isLiteral(Kind) && "Literals are not supported.");
-    assert(!tok::isAnnotation(Kind) && "Annotations are not supported.");
-  }
-  TokenValue(IdentifierInfo *II) : Kind(tok::identifier), II(II) {}
-  bool operator==(const Token &Tok) const {
-    return Tok.getKind() == Kind &&
-        (!II || II == Tok.getIdentifierInfo());
-  }
-};
-
-/// \brief Compares macro tokens with a specified token value sequence.
-static bool MacroDefinitionEquals(const MacroInfo *MI,
-                                  llvm::ArrayRef<TokenValue> Tokens) {
-  return Tokens.size() == MI->getNumTokens() &&
-      std::equal(Tokens.begin(), Tokens.end(), MI->tokens_begin());
-}
-
-static std::string GetSuitableSpelling(Preprocessor &PP, SourceLocation L,
-                                       llvm::ArrayRef<TokenValue> Tokens,
-                                       const char *Spelling) {
-  SourceManager &SM = PP.getSourceManager();
-  SourceLocation BestLocation;
-  std::string BestSpelling = Spelling;
-  for (Preprocessor::macro_iterator I = PP.macro_begin(), E = PP.macro_end();
-       I != E; ++I) {
-    if (!I->second->isObjectLike())
-      continue;
-    const MacroInfo *MI = I->second->findDefinitionAtLoc(L, SM);
-    if (!MI)
-      continue;
-    if (!MacroDefinitionEquals(MI, Tokens))
-      continue;
-    SourceLocation Location = I->second->getDefinitionLoc();
-    // Choose the macro defined latest.
-    if (BestLocation.isInvalid() ||
-        (Location.isValid() &&
-         SM.isBeforeInTranslationUnit(BestLocation, Location))) {
-      BestLocation = Location;
-      BestSpelling = I->first->getName();
-    }
-  }
-  return BestSpelling;
-}
-
 namespace {
   class FallthroughMapper : public RecursiveASTVisitor<FallthroughMapper> {
   public:
@@ -914,11 +860,15 @@ static void DiagnoseSwitchLabelsFallthrough(Sema &S, AnalysisDeclContext &AC,
             tok::coloncolon, PP.getIdentifierInfo("fallthrough"),
             tok::r_square, tok::r_square
           };
-          std::string AnnotationSpelling = GetSuitableSpelling(
-              PP, L, Tokens, "[[clang::fallthrough]]");
+          StringRef AnnotationSpelling = "[[clang::fallthrough]]";
+          StringRef MacroName = PP.getLastMacroWithSpelling(L, Tokens);
+          if (!MacroName.empty())
+            AnnotationSpelling = MacroName;
+          SmallString<64> TextToInsert(AnnotationSpelling);
+          TextToInsert += "; ";
           S.Diag(L, diag::note_insert_fallthrough_fixit) <<
               AnnotationSpelling <<
-              FixItHint::CreateInsertion(L, AnnotationSpelling + "; ");
+              FixItHint::CreateInsertion(L, TextToInsert);
         }
       }
       S.Diag(L, diag::note_insert_break_fixit) <<
index 592956b..019a7f1 100644 (file)
@@ -7266,7 +7266,7 @@ void Sema::ActOnDocumentableDecls(Decl **Group, unsigned NumDecls) {
     // the lookahead in the lexer: we've consumed the semicolon and looked
     // ahead through comments.
     for (unsigned i = 0; i != NumDecls; ++i)
-      Context.getCommentForDecl(Group[i]);
+      Context.getCommentForDecl(Group[i], &PP);
   }
 }
 
index 812d404..a47b776 100644 (file)
@@ -51,6 +51,12 @@ struct test_deprecated_6 {
   }
 };
 
+#define MY_ATTR_DEPRECATED __attribute__((deprecated))
+
+// 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_9(int a);
+
 // CHECK: fix-it:"{{.*}}":{5:12-5:22}:"a"
 // CHECK: fix-it:"{{.*}}":{9:12-9:15}:"aaa"
 // CHECK: fix-it:"{{.*}}":{13:13-13:23}:"T"
@@ -61,4 +67,5 @@ struct test_deprecated_6 {
 // 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))"
+// CHECK: fix-it:"{{.*}}":{58:30-58:30}:" MY_ATTR_DEPRECATED"
 
index f1080f6..5ad025a 100644 (file)
@@ -5810,7 +5810,7 @@ CXComment clang_Cursor_getParsedComment(CXCursor C) {
 
   const Decl *D = getCursorDecl(C);
   const ASTContext &Context = getCursorContext(C);
-  const comments::FullComment *FC = Context.getCommentForDecl(D);
+  const comments::FullComment *FC = Context.getCommentForDecl(D, /*PP=*/ NULL);
 
   return cxcomment::createCXComment(FC, getCursorTU(C));
 }
index 0ed8771..d6b1f58 100644 (file)
@@ -59,7 +59,7 @@ FullComment *CommentParserTest::parseString(const char *Source) {
 
   Lexer L(Allocator, Traits, Begin, Source, Source + strlen(Source));
 
-  Sema S(Allocator, SourceMgr, Diags, Traits);
+  Sema S(Allocator, SourceMgr, Diags, Traits, /*PP=*/ NULL);
   Parser P(L, S, Allocator, SourceMgr, Diags, Traits);
   FullComment *FC = P.parseFullComment();