aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2010-03-18 17:52:52 +0000
committerDouglas Gregor <dgregor@apple.com>2010-03-18 17:52:52 +0000
commit4ae8f298b1ea51b4c2234f9148e2e4349c9bdd23 (patch)
tree115d68779b45333b43c5c5937a41f292b58dc666
parent7c647a1270cbe02f5e64cee9e3f3ddc176ed61e9 (diff)
Introduce the notion of a "preprocessing record", which keeps track of
the macro definitions and macro instantiations that are found during preprocessing. Preprocessing records are *not* generated by default; rather, we provide a PPCallbacks subclass that hooks into the existing callback mechanism to record this activity. The only client of preprocessing records is CIndex, which keeps track of macro definitions and instantations so that they can be exposed via cursors. At present, only token annotation uses these facilities, and only for macro instantiations; both will change in the near future. However, with this change, token annotation properly annotates macro instantiations that do not produce any tokens and instantiations of macros that are later undef'd, improving our consistency. Preprocessing directives that are not macro definitions are still handled by clang_annotateTokens() via re-lexing, so that we don't have to track every preprocessing directive in the preprocessing record. Performance impact of preprocessing records is still TBD, although it is limited to CIndex and therefore out of the path of the main compiler. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@98836 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Frontend/ASTUnit.h16
-rw-r--r--include/clang/Lex/PreprocessingRecord.h235
-rw-r--r--lib/Frontend/ASTUnit.cpp18
-rw-r--r--lib/Lex/CMakeLists.txt1
-rw-r--r--lib/Lex/PreprocessingRecord.cpp37
-rw-r--r--test/Index/annotate-tokens-pp.c78
-rw-r--r--tools/CIndex/CIndex.cpp138
-rw-r--r--tools/CIndex/CXCursor.cpp15
-rw-r--r--tools/CIndex/CXCursor.h5
9 files changed, 415 insertions, 128 deletions
diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h
index 27ec12e4e4..0616d03226 100644
--- a/include/clang/Frontend/ASTUnit.h
+++ b/include/clang/Frontend/ASTUnit.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_FRONTEND_ASTUNIT_H
#define LLVM_CLANG_FRONTEND_ASTUNIT_H
+#include "clang/Lex/PreprocessingRecord.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/OwningPtr.h"
#include "clang/Basic/FileManager.h"
@@ -53,7 +54,8 @@ class ASTUnit {
llvm::OwningPtr<TargetInfo> Target;
llvm::OwningPtr<Preprocessor> PP;
llvm::OwningPtr<ASTContext> Ctx;
-
+ llvm::OwningPtr<PreprocessingRecord> Preprocessing;
+
/// Optional owned invocation, just used to make the invocation used in
/// LoadFromCommandLine available.
llvm::OwningPtr<CompilerInvocation> Invocation;
@@ -135,6 +137,12 @@ public:
const ASTContext &getASTContext() const { return *Ctx.get(); }
ASTContext &getASTContext() { return *Ctx.get(); }
+ const PreprocessingRecord &getPreprocessingRecord() const {
+ return *Preprocessing.get();
+ }
+ PreprocessingRecord &getPreprocessingRecord() { return *Preprocessing.get(); }
+ bool hasPreprocessingRecord() { return Preprocessing.get() != 0; }
+
const FileManager &getFileManager() const { return FileMgr; }
FileManager &getFileManager() { return FileMgr; }
@@ -204,7 +212,8 @@ public:
static ASTUnit *LoadFromCompilerInvocation(CompilerInvocation *CI,
Diagnostic &Diags,
bool OnlyLocalDecls = false,
- bool CaptureDiagnostics = false);
+ bool CaptureDiagnostics = false,
+ bool WantPreprocessingRecord = false);
/// LoadFromCommandLine - Create an ASTUnit from a vector of command line
/// arguments, which must specify exactly one source file.
@@ -227,7 +236,8 @@ public:
bool OnlyLocalDecls = false,
RemappedFile *RemappedFiles = 0,
unsigned NumRemappedFiles = 0,
- bool CaptureDiagnostics = false);
+ bool CaptureDiagnostics = false,
+ bool WantPreprocessingRecord = false);
};
} // namespace clang
diff --git a/include/clang/Lex/PreprocessingRecord.h b/include/clang/Lex/PreprocessingRecord.h
new file mode 100644
index 0000000000..4ebd4e6039
--- /dev/null
+++ b/include/clang/Lex/PreprocessingRecord.h
@@ -0,0 +1,235 @@
+//===--- PreprocessingRecord.h - Record of Preprocessing --------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the PreprocessingRecord class, which maintains a record
+// of what occurred during preprocessing.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_LEX_PREPROCESSINGRECORD_H
+#define LLVM_CLANG_LEX_PREPROCESSINGRECORD_H
+
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/Support/Allocator.h"
+#include <vector>
+
+namespace clang {
+ class IdentifierInfo;
+ class PreprocessingRecord;
+}
+
+/// \brief Allocates memory within a Clang preprocessing record.
+void* operator new(size_t bytes, clang::PreprocessingRecord& PR,
+ unsigned alignment = 8) throw();
+
+/// \brief Frees memory allocated in a Clang preprocessing record.
+void operator delete(void* ptr, clang::PreprocessingRecord& PR,
+ unsigned) throw();
+
+namespace clang {
+ /// \brief Base class that describes a preprocessed entity, which may be a
+ /// preprocessor directive or macro instantiation.
+ class PreprocessedEntity {
+ public:
+ /// \brief The kind of preprocessed entity an object describes.
+ enum EntityKind {
+ /// \brief A macro instantiation.
+ MacroInstantiationKind,
+
+ /// \brief A preprocessing directive whose kind is not specified.
+ ///
+ /// This kind will be used for any preprocessing directive that does not
+ /// have a more specific kind within the \c DirectiveKind enumeration.
+ PreprocessingDirectiveKind,
+
+ /// \brief A macro definition.
+ MacroDefinitionKind,
+
+ FirstPreprocessingDirective = PreprocessingDirectiveKind,
+ LastPreprocessingDirective = MacroDefinitionKind
+ };
+
+ private:
+ /// \brief The kind of preprocessed entity that this object describes.
+ EntityKind Kind;
+
+ /// \brief The source range that covers this preprocessed entity.
+ SourceRange Range;
+
+ protected:
+ PreprocessedEntity(EntityKind Kind, SourceRange Range)
+ : Kind(Kind), Range(Range) { }
+
+ public:
+ /// \brief Retrieve the kind of preprocessed entity stored in this object.
+ EntityKind getKind() const { return Kind; }
+
+ /// \brief Retrieve the source range that covers this entire preprocessed
+ /// entity.
+ SourceRange getSourceRange() const { return Range; }
+
+ // Implement isa/cast/dyncast/etc.
+ static bool classof(const PreprocessedEntity *) { return true; }
+
+ // Only allow allocation of preprocessed entities using the allocator
+ // in PreprocessingRecord or by doing a placement new.
+ void* operator new(size_t bytes, PreprocessingRecord& PR,
+ unsigned alignment = 8) throw() {
+ return ::operator new(bytes, PR, alignment);
+ }
+
+ void* operator new(size_t bytes, void* mem) throw() {
+ return mem;
+ }
+
+ void operator delete(void* ptr, PreprocessingRecord& PR,
+ unsigned alignment) throw() {
+ return ::operator delete(ptr, PR, alignment);
+ }
+
+ void operator delete(void*, std::size_t) throw() { }
+ void operator delete(void*, void*) throw() { }
+
+ private:
+ // Make vanilla 'new' and 'delete' illegal for preprocessed entities.
+ void* operator new(size_t bytes) throw();
+ void operator delete(void* data) throw();
+ };
+
+ /// \brief Records the location of a macro instantiation.
+ class MacroInstantiation : public PreprocessedEntity {
+ /// \brief The name of the macro being instantiation.
+ IdentifierInfo *Name;
+
+ /// \brief The location of the definition of the macro being instantiated.
+ SourceLocation DefinitionLocation;
+
+ public:
+ MacroInstantiation(IdentifierInfo *Name, SourceRange Range,
+ SourceLocation DefinitionLocation)
+ : PreprocessedEntity(MacroInstantiationKind, Range), Name(Name),
+ DefinitionLocation(DefinitionLocation) { }
+
+ /// \brief The name of the macro being instantiated.
+ IdentifierInfo *getName() const { return Name; }
+
+ /// \brief The location of the definition of the macro being instantiated.
+ /// FIXME: Could we just provide MacroInfo pointers instead, by teaching
+ /// the preprocessor to hold on to them when we care to keep them around?
+ SourceLocation getDefinitionLocation() const { return DefinitionLocation; }
+
+ // Implement isa/cast/dyncast/etc.
+ static bool classof(const PreprocessedEntity *PE) {
+ return PE->getKind() == MacroInstantiationKind;
+ }
+ static bool classof(const MacroInstantiation *) { return true; }
+
+ };
+
+ /// \brief Records the presence of a preprocessor directive.
+ class PreprocessingDirective : public PreprocessedEntity {
+ public:
+ PreprocessingDirective(EntityKind Kind, SourceRange Range)
+ : PreprocessedEntity(Kind, Range) { }
+
+ // Implement isa/cast/dyncast/etc.
+ static bool classof(const PreprocessedEntity *PD) {
+ return PD->getKind() >= FirstPreprocessingDirective &&
+ PD->getKind() <= LastPreprocessingDirective;
+ }
+ static bool classof(const PreprocessingDirective *) { return true; }
+ };
+
+ /// \brief Record the location of a macro definition.
+ class MacroDefinition : public PreprocessingDirective {
+ /// \brief The name of the macro being defined.
+ const IdentifierInfo *Name;
+
+ /// \brief The location of the macro name in the macro definition.
+ SourceLocation Location;
+
+ public:
+ explicit MacroDefinition(const IdentifierInfo *Name, SourceLocation Location,
+ SourceRange Range)
+ : PreprocessingDirective(MacroDefinitionKind, Range), Name(Name),
+ Location(Location) { }
+
+ /// \brief Retrieve the name of the macro being defined.
+ const IdentifierInfo *getMacroName() const { return Name; }
+
+ /// \brief Retrieve the location of the macro name in the definition.
+ SourceLocation getLocation() const { return Location; }
+
+ // Implement isa/cast/dyncast/etc.
+ static bool classof(const PreprocessingDirective *PD) {
+ return PD->getKind() == MacroDefinitionKind;
+ }
+ static bool classof(const MacroDefinition *) { return true; }
+ };
+
+ /// \brief A record of the steps taken while preprocessing a source file,
+ /// including the various preprocessing directives processed, macros
+ /// instantiated, etc.
+ class PreprocessingRecord {
+ /// \brief Allocator used to store preprocessing objects.
+ llvm::BumpPtrAllocator BumpAlloc;
+
+ /// \brief The set of preprocessed entities in this record, in order they
+ /// were seen.
+ std::vector<PreprocessedEntity *> PreprocessedEntities;
+
+ public:
+ /// \brief Allocate memory in the preprocessing record.
+ void *Allocate(unsigned Size, unsigned Align = 8) {
+ return BumpAlloc.Allocate(Size, Align);
+ }
+
+ /// \brief Deallocate memory in the preprocessing record.
+ void Deallocate(void *Ptr) { }
+
+ // Iteration over the preprocessed entities.
+ typedef std::vector<PreprocessedEntity *>::iterator iterator;
+ typedef std::vector<PreprocessedEntity *>::const_iterator const_iterator;
+ iterator begin() { return PreprocessedEntities.begin(); }
+ iterator end() { return PreprocessedEntities.end(); }
+ const_iterator begin() const { return PreprocessedEntities.begin(); }
+ const_iterator end() const { return PreprocessedEntities.end(); }
+
+ /// \brief Add a new preprocessed entity to this record.
+ void addPreprocessedEntity(PreprocessedEntity *Entity);
+ };
+
+ /// \brief Preprocessor callback action used to populate a preprocessing
+ /// record.
+ class PopulatePreprocessingRecord : public PPCallbacks {
+ /// \brief The preprocessing record this action will populate.
+ PreprocessingRecord &Record;
+
+ public:
+ explicit PopulatePreprocessingRecord(PreprocessingRecord &Record)
+ : Record(Record) { }
+
+ virtual void MacroExpands(const Token &Id, const MacroInfo* MI);
+ virtual void MacroDefined(const IdentifierInfo *II, const MacroInfo *MI);
+ };
+
+} // end namespace clang
+
+inline void* operator new(size_t bytes, clang::PreprocessingRecord& PR,
+ unsigned alignment) throw() {
+ return PR.Allocate(bytes, alignment);
+}
+
+inline void operator delete(void* ptr, clang::PreprocessingRecord& PR,
+ unsigned) throw() {
+ PR.Deallocate(ptr);
+}
+
+#endif // LLVM_CLANG_LEX_PREPROCESSINGRECORD_H
diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp
index 935c415243..1aaa536cb3 100644
--- a/lib/Frontend/ASTUnit.cpp
+++ b/lib/Frontend/ASTUnit.cpp
@@ -278,7 +278,8 @@ public:
ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI,
Diagnostic &Diags,
bool OnlyLocalDecls,
- bool CaptureDiagnostics) {
+ bool CaptureDiagnostics,
+ bool WantPreprocessingRecord) {
// Create the compiler instance to use for building the AST.
CompilerInstance Clang;
llvm::OwningPtr<ASTUnit> AST;
@@ -328,6 +329,15 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI,
// Create the preprocessor.
Clang.createPreprocessor();
+ // If the ASTUnit was requested to store information about preprocessing,
+ // create storage for that information and attach an appropriate callback to
+ // populate that storage.
+ if (WantPreprocessingRecord) {
+ AST->Preprocessing.reset(new PreprocessingRecord);
+ Clang.getPreprocessor().addPPCallbacks(
+ new PopulatePreprocessingRecord(*AST->Preprocessing));
+ }
+
Act.reset(new TopLevelDeclTrackerAction(*AST));
if (!Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second,
/*IsAST=*/false))
@@ -367,7 +377,8 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin,
bool OnlyLocalDecls,
RemappedFile *RemappedFiles,
unsigned NumRemappedFiles,
- bool CaptureDiagnostics) {
+ bool CaptureDiagnostics,
+ bool WantPreprocessingRecord) {
llvm::SmallVector<const char *, 16> Args;
Args.push_back("<clang>"); // FIXME: Remove dummy argument.
Args.insert(Args.end(), ArgBegin, ArgEnd);
@@ -419,5 +430,6 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin,
CI->getFrontendOpts().DisableFree = true;
return LoadFromCompilerInvocation(CI.take(), Diags, OnlyLocalDecls,
- CaptureDiagnostics);
+ CaptureDiagnostics,
+ WantPreprocessingRecord);
}
diff --git a/lib/Lex/CMakeLists.txt b/lib/Lex/CMakeLists.txt
index 81a1e01f96..632fbc6340 100644
--- a/lib/Lex/CMakeLists.txt
+++ b/lib/Lex/CMakeLists.txt
@@ -16,6 +16,7 @@ add_clang_library(clangLex
PPMacroExpansion.cpp
PTHLexer.cpp
Pragma.cpp
+ PreprocessingRecord.cpp
Preprocessor.cpp
PreprocessorLexer.cpp
ScratchBuffer.cpp
diff --git a/lib/Lex/PreprocessingRecord.cpp b/lib/Lex/PreprocessingRecord.cpp
new file mode 100644
index 0000000000..fe081b69bb
--- /dev/null
+++ b/lib/Lex/PreprocessingRecord.cpp
@@ -0,0 +1,37 @@
+//===--- PreprocessingRecord.cpp - Record of Preprocessing ------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the PreprocessingRecord class, which maintains a record
+// of what occurred during preprocessing, and its helpers.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/Lex/PreprocessingRecord.h"
+#include "clang/Lex/MacroInfo.h"
+#include "clang/Lex/Token.h"
+
+using namespace clang;
+
+void PreprocessingRecord::addPreprocessedEntity(PreprocessedEntity *Entity) {
+ PreprocessedEntities.push_back(Entity);
+}
+
+void PopulatePreprocessingRecord::MacroExpands(const Token &Id,
+ const MacroInfo* MI) {
+ Record.addPreprocessedEntity(
+ new (Record) MacroInstantiation(Id.getIdentifierInfo(),
+ Id.getLocation(),
+ MI->getDefinitionLoc()));
+}
+
+void PopulatePreprocessingRecord::MacroDefined(const IdentifierInfo *II,
+ const MacroInfo *MI) {
+ SourceRange R(MI->getDefinitionLoc(), MI->getDefinitionEndLoc());
+ Record.addPreprocessedEntity(
+ new (Record) MacroDefinition(II, MI->getDefinitionLoc(), R));
+}
diff --git a/test/Index/annotate-tokens-pp.c b/test/Index/annotate-tokens-pp.c
index e646948c89..db808d0943 100644
--- a/test/Index/annotate-tokens-pp.c
+++ b/test/Index/annotate-tokens-pp.c
@@ -1,32 +1,58 @@
+#define NOTHING(X,Y)
+#define STILL_NOTHING NOTHING(honk,warble)
#define BAR baz
#define WIBBLE(X, Y) X##Y
-float WIBBLE(int, float);
-int BAR;
+NOTHING(more,junk) float WIBBLE(int, float);
+int BAR STILL_NOTHING;
#include "foo.h"
+#undef BAR
-// RUN: c-index-test -test-annotate-tokens=%s:1:1:6:1 -I%S/Inputs %s | FileCheck %s
-// CHECK: Punctuation: "#" [1:1 - 1:2] preprocessing directive=
-// CHECK: Identifier: "define" [1:2 - 1:8] preprocessing directive=
-// CHECK: Identifier: "BAR" [1:9 - 1:12] preprocessing directive=
-// CHECK: Identifier: "baz" [1:13 - 1:16] preprocessing directive=
+// RUN: c-index-test -test-annotate-tokens=%s:2:1:9:1 -I%S/Inputs %s | FileCheck %s
// CHECK: Punctuation: "#" [2:1 - 2:2] preprocessing directive=
// CHECK: Identifier: "define" [2:2 - 2:8] preprocessing directive=
-// CHECK: Identifier: "WIBBLE" [2:9 - 2:15] preprocessing directive=
-// CHECK: Punctuation: "(" [2:15 - 2:16] preprocessing directive=
-// CHECK: Identifier: "X" [2:16 - 2:17] preprocessing directive=
-// CHECK: Punctuation: "," [2:17 - 2:18] preprocessing directive=
-// CHECK: Identifier: "Y" [2:19 - 2:20] preprocessing directive=
-// CHECK: Punctuation: ")" [2:20 - 2:21] preprocessing directive=
-// CHECK: Identifier: "WIBBLE" [3:7 - 3:13] macro instantiation=
-// CHECK: Punctuation: "(" [3:13 - 3:14]
-// CHECK: Keyword: "int" [3:14 - 3:17]
-// CHECK: Punctuation: "," [3:17 - 3:18]
-// CHECK: Keyword: "float" [3:19 - 3:24]
-// CHECK: Punctuation: ")" [3:24 - 3:25]
-// CHECK: Punctuation: ";" [3:25 - 3:26]
-// CHECK: Keyword: "int" [4:1 - 4:4]
-// CHECK: Identifier: "BAR" [4:5 - 4:8] macro instantiation=
-// CHECK: Punctuation: ";" [4:8 - 4:9]
-// CHECK: Punctuation: "#" [5:1 - 5:2] preprocessing directive=
-// CHECK: Identifier: "include" [5:2 - 5:9] preprocessing directive=
-// CHECK: Literal: ""foo.h"" [5:10 - 5:17] preprocessing directive=
+// CHECK: Identifier: "STILL_NOTHING" [2:9 - 2:22] preprocessing directive=
+// CHECK: Identifier: "NOTHING" [2:23 - 2:30] preprocessing directive=
+// CHECK: Punctuation: "(" [2:30 - 2:31] preprocessing directive=
+// CHECK: Identifier: "honk" [2:31 - 2:35] preprocessing directive=
+// CHECK: Punctuation: "," [2:35 - 2:36] preprocessing directive=
+// CHECK: Identifier: "warble" [2:36 - 2:42] preprocessing directive=
+// CHECK: Punctuation: ")" [2:42 - 2:43] preprocessing directive=
+// CHECK: Punctuation: "#" [3:1 - 3:2] preprocessing directive=
+// CHECK: Identifier: "define" [3:2 - 3:8] preprocessing directive=
+// CHECK: Identifier: "BAR" [3:9 - 3:12] preprocessing directive=
+// CHECK: Identifier: "baz" [3:13 - 3:16] preprocessing directive=
+// CHECK: Punctuation: "#" [4:1 - 4:2] preprocessing directive=
+// CHECK: Identifier: "define" [4:2 - 4:8] preprocessing directive=
+// CHECK: Identifier: "WIBBLE" [4:9 - 4:15] preprocessing directive=
+// CHECK: Punctuation: "(" [4:15 - 4:16] preprocessing directive=
+// CHECK: Identifier: "X" [4:16 - 4:17] preprocessing directive=
+// CHECK: Punctuation: "," [4:17 - 4:18] preprocessing directive=
+// CHECK: Identifier: "Y" [4:19 - 4:20] preprocessing directive=
+// CHECK: Punctuation: ")" [4:20 - 4:21] preprocessing directive=
+// CHECK: Identifier: "X" [4:22 - 4:23] preprocessing directive=
+// CHECK: Punctuation: "##" [4:23 - 4:25] preprocessing directive=
+// CHECK: Identifier: "Y" [4:25 - 4:26] preprocessing directive=
+// CHECK: Identifier: "NOTHING" [5:1 - 5:8] macro instantiation=NOTHING
+// CHECK: Punctuation: "(" [5:8 - 5:9]
+// CHECK: Identifier: "more" [5:9 - 5:13]
+// CHECK: Punctuation: "," [5:13 - 5:14]
+// CHECK: Identifier: "junk" [5:14 - 5:18]
+// CHECK: Punctuation: ")" [5:18 - 5:19]
+// CHECK: Keyword: "float" [5:20 - 5:25]
+// CHECK: Identifier: "WIBBLE" [5:26 - 5:32] macro instantiation=WIBBLE
+// CHECK: Punctuation: "(" [5:32 - 5:33]
+// CHECK: Keyword: "int" [5:33 - 5:36]
+// CHECK: Punctuation: "," [5:36 - 5:37]
+// CHECK: Keyword: "float" [5:38 - 5:43]
+// CHECK: Punctuation: ")" [5:43 - 5:44]
+// CHECK: Punctuation: ";" [5:44 - 5:45]
+// CHECK: Keyword: "int" [6:1 - 6:4]
+// CHECK: Identifier: "BAR" [6:5 - 6:8] macro instantiation=BAR
+// CHECK: Identifier: "STILL_NOTHING" [6:9 - 6:22] macro instantiation=STILL_NOTHING
+// CHECK: Punctuation: ";" [6:22 - 6:23]
+// CHECK: Punctuation: "#" [7:1 - 7:2] preprocessing directive=
+// CHECK: Identifier: "include" [7:2 - 7:9] preprocessing directive=
+// CHECK: Literal: ""foo.h"" [7:10 - 7:17] preprocessing directive=
+// CHECK: Punctuation: "#" [8:1 - 8:2] preprocessing directive=
+// CHECK: Identifier: "undef" [8:2 - 8:7] preprocessing directive=
+// CHECK: Identifier: "BAR" [8:8 - 8:11] preprocessing directive=
diff --git a/tools/CIndex/CIndex.cpp b/tools/CIndex/CIndex.cpp
index ed0c562eb0..deab84698c 100644
--- a/tools/CIndex/CIndex.cpp
+++ b/tools/CIndex/CIndex.cpp
@@ -1008,7 +1008,8 @@ clang_createTranslationUnitFromSourceFile(CXIndex CIdx,
CXXIdx->getOnlyLocalDecls(),
RemappedFiles.data(),
RemappedFiles.size(),
- /*CaptureDiagnostics=*/true));
+ /*CaptureDiagnostics=*/true,
+ /*WantPreprocessingRecord=*/true));
// FIXME: Until we have broader testing, just drop the entire AST if we
// encountered an error.
@@ -1438,6 +1439,10 @@ CXString clang_getCursorSpelling(CXCursor C) {
return createCXString("");
}
+ if (C.kind == CXCursor_MacroInstantiation)
+ return createCXString(getCursorMacroInstantiation(C)->getName()
+ ->getNameStart());
+
if (clang_isDeclaration(C.kind))
return getDeclSpelling(getCursorDecl(C));
@@ -1656,7 +1661,8 @@ CXSourceLocation clang_getCursorLocation(CXCursor C) {
}
if (C.kind == CXCursor_MacroInstantiation) {
- SourceLocation L = cxcursor::getCursorMacroInstantiation(C).getBegin();
+ SourceLocation L
+ = cxcursor::getCursorMacroInstantiation(C)->getSourceRange().getBegin();
return cxloc::translateSourceLocation(getCursorContext(C), L);
}
@@ -1717,7 +1723,7 @@ CXSourceRange clang_getCursorExtent(CXCursor C) {
}
if (C.kind == CXCursor_MacroInstantiation) {
- SourceRange R = cxcursor::getCursorMacroInstantiation(C);
+ SourceRange R = cxcursor::getCursorMacroInstantiation(C)->getSourceRange();
return cxloc::translateSourceRange(getCursorContext(C), R);
}
@@ -2030,16 +2036,36 @@ void clang_enableStackTraces(void) {
//===----------------------------------------------------------------------===//
namespace {
-/// IgnoringDiagClient - This is a diagnostic client that just ignores all
-/// diags.
-class IgnoringDiagClient : public DiagnosticClient {
- void HandleDiagnostic(Diagnostic::Level DiagLevel,
- const DiagnosticInfo &Info) {
- // Just ignore it.
- }
-};
+ class ComparePreprocessedEntityLocation {
+ SourceManager &SM;
+
+ public:
+ explicit ComparePreprocessedEntityLocation(SourceManager &SM) : SM(SM) { }
+
+ bool operator()(const PreprocessedEntity *Entity, SourceLocation Loc) {
+ return SM.isBeforeInTranslationUnit(Entity->getSourceRange().getEnd(),
+ Loc);
+ }
+
+ bool operator()(SourceLocation Loc, const PreprocessedEntity *Entity) {
+ return SM.isBeforeInTranslationUnit(Loc,
+ Entity->getSourceRange().getBegin());
+ }
+
+ bool operator()(const PreprocessedEntity *Entity, SourceRange R) {
+ return SM.isBeforeInTranslationUnit(Entity->getSourceRange().getEnd(),
+ R.getBegin());
+ }
+
+ bool operator()(SourceRange R, const PreprocessedEntity *Entity) {
+ return SM.isBeforeInTranslationUnit(R.getEnd(),
+ Entity->getSourceRange().getBegin());
+ }
+ };
}
+
+
/* CXToken layout:
* int_data[0]: a CXTokenKind
* int_data[1]: starting token location
@@ -2298,13 +2324,13 @@ void clang_annotateTokens(CXTranslationUnit TU,
if (RelexOkay) {
Lexer Lex(SourceMgr.getLocForStartOfFile(BeginLocInfo.first),
CXXUnit->getASTContext().getLangOptions(),
- Buffer.begin(), Buffer.data() + BeginLocInfo.second, Buffer.end());
+ Buffer.begin(), Buffer.data() + BeginLocInfo.second,
+ Buffer.end());
Lex.SetCommentRetentionState(true);
// Lex tokens in raw mode until we hit the end of the range, to avoid
// entering #includes or expanding macros.
std::vector<Token> TokenStream;
- Preprocessor &PP = CXXUnit->getPreprocessor();
while (true) {
Token Tok;
Lex.LexFromRawLexer(Tok);
@@ -2339,84 +2365,30 @@ void clang_annotateTokens(CXTranslationUnit TU,
continue;
}
- // If this is a ## token, change its kind to unknown so that
- // repreprocessing it will not produce an error.
- if (Tok.is(tok::hashhash))
- Tok.setKind(tok::unknown);
-
- // If this raw token is an identifier, the raw lexer won't have
- // looked up the corresponding identifier info for it. Do this
- // now so that it will be macro expanded when we re-preprocess
- // it.
- if (Tok.is(tok::identifier)) {
- // Change the kind of this identifier to the appropriate token kind, e.g.
- // turning "for" into a keyword.
- Tok.setKind(PP.LookUpIdentifierInfo(Tok)->getTokenID());
- }
-
- TokenStream.push_back(Tok);
-
if (Tok.is(tok::eof))
break;
}
+ }
+
+ if (CXXUnit->hasPreprocessingRecord()) {
+ PreprocessingRecord &PPRec = CXXUnit->getPreprocessingRecord();
+ std::pair<PreprocessingRecord::iterator, PreprocessingRecord::iterator>
+ Entities = std::equal_range(PPRec.begin(), PPRec.end(), RegionOfInterest,
+ ComparePreprocessedEntityLocation(SourceMgr));
+ for (; Entities.first != Entities.second; ++Entities.first) {
+ PreprocessedEntity *Entity = *Entities.first;
+ if (MacroInstantiation *MI = dyn_cast<MacroInstantiation>(Entity)) {
+ SourceLocation Loc = MI->getSourceRange().getBegin();
+ if (Loc.isFileID()) {
+ Annotated[Loc.getRawEncoding()]
+ = MakeMacroInstantiationCursor(MI, CXXUnit);
+ }
- // Temporarily change the diagnostics object so that we ignore any
- // generated diagnostics from this pass.
- IgnoringDiagClient TmpDC;
- Diagnostic TmpDiags(&TmpDC);
- Diagnostic *OldDiags = &PP.getDiagnostics();
- PP.setDiagnostics(TmpDiags);
-
- // Inform the preprocessor that we don't want comments.
- PP.SetCommentRetentionState(false, false);
-
- // Enter the tokens we just lexed. This will cause them to be macro expanded
- // but won't enter sub-files (because we removed #'s).
- PP.EnterTokenStream(&TokenStream[0], TokenStream.size(), false, false);
-
- // Lex all the tokens.
- Token Tok;
- PP.Lex(Tok);
- while (Tok.isNot(tok::eof)) {
- // Ignore non-macro tokens.
- if (!Tok.getLocation().isMacroID()) {
- PP.Lex(Tok);
- continue;
- }
-
- // Okay, we have the first token of a macro expansion. Keep
- // track of the range of the macro expansion.
- std::pair<SourceLocation, SourceLocation> LLoc =
- SourceMgr.getInstantiationRange(Tok.getLocation());
-
- // Ignore tokens whose instantiation location was not the main file.
- if (SourceMgr.getFileID(LLoc.first) != BeginLocInfo.first) {
- PP.Lex(Tok);
continue;
}
- assert(SourceMgr.getFileID(LLoc.second) == BeginLocInfo.first &&
- "Start and end of expansion must be in the same ultimate file!");
-
- // Okay, eat this token, getting the next one.
- PP.Lex(Tok);
-
- // Skip all the rest of the tokens that are part of this macro
- // instantiation. It would be really nice to pop up a window with all the
- // spelling of the tokens or something.
- while (!Tok.is(tok::eof) &&
- SourceMgr.getInstantiationLoc(Tok.getLocation()) == LLoc.first)
- PP.Lex(Tok);
-
- CXCursor Cursor
- = cxcursor::MakeMacroInstantiationCursor(SourceRange(LLoc.first,
- LLoc.second),
- CXXUnit);
- Annotated[LLoc.first.getRawEncoding()] = Cursor;
+ // FIXME: expose other preprocessed entities.
}
-
- // Restore diagnostics object back to its own thing.
- PP.setDiagnostics(*OldDiags);
}
for (unsigned I = 0; I != NumTokens; ++I) {
diff --git a/tools/CIndex/CXCursor.cpp b/tools/CIndex/CXCursor.cpp
index aa81d60f61..1e5265ee9a 100644
--- a/tools/CIndex/CXCursor.cpp
+++ b/tools/CIndex/CXCursor.cpp
@@ -314,22 +314,15 @@ SourceRange cxcursor::getCursorPreprocessingDirective(CXCursor C) {
reinterpret_cast<uintptr_t> (C.data[1])));
}
-CXCursor cxcursor::MakeMacroInstantiationCursor(SourceRange Range,
+CXCursor cxcursor::MakeMacroInstantiationCursor(MacroInstantiation *MI,
ASTUnit *TU) {
- CXCursor C = { CXCursor_MacroInstantiation,
- { reinterpret_cast<void *>(Range.getBegin().getRawEncoding()),
- reinterpret_cast<void *>(Range.getEnd().getRawEncoding()),
- TU }
- };
+ CXCursor C = { CXCursor_MacroInstantiation, { MI, 0, TU } };
return C;
}
-SourceRange cxcursor::getCursorMacroInstantiation(CXCursor C) {
+MacroInstantiation *cxcursor::getCursorMacroInstantiation(CXCursor C) {
assert(C.kind == CXCursor_MacroInstantiation);
- return SourceRange(SourceLocation::getFromRawEncoding(
- reinterpret_cast<uintptr_t> (C.data[0])),
- SourceLocation::getFromRawEncoding(
- reinterpret_cast<uintptr_t> (C.data[1])));
+ return static_cast<MacroInstantiation *>(C.data[0]);
}
Decl *cxcursor::getCursorDecl(CXCursor Cursor) {
diff --git a/tools/CIndex/CXCursor.h b/tools/CIndex/CXCursor.h
index 12103b6d7b..24ac1e414e 100644
--- a/tools/CIndex/CXCursor.h
+++ b/tools/CIndex/CXCursor.h
@@ -25,6 +25,7 @@ class ASTUnit;
class Attr;
class Decl;
class Expr;
+class MacroInstantiation;
class NamedDecl;
class ObjCInterfaceDecl;
class ObjCProtocolDecl;
@@ -80,11 +81,11 @@ CXCursor MakePreprocessingDirectiveCursor(SourceRange Range, ASTUnit *TU);
SourceRange getCursorPreprocessingDirective(CXCursor C);
/// \brief Create a macro instantiation cursor.
-CXCursor MakeMacroInstantiationCursor(SourceRange Range, ASTUnit *TU);
+CXCursor MakeMacroInstantiationCursor(MacroInstantiation *, ASTUnit *TU);
/// \brief Unpack a given macro instantiation cursor to retrieve its
/// source range.
-SourceRange getCursorMacroInstantiation(CXCursor C);
+MacroInstantiation *getCursorMacroInstantiation(CXCursor C);
Decl *getCursorDecl(CXCursor Cursor);
Expr *getCursorExpr(CXCursor Cursor);