#define LLVM_CLANG_BASIC_DIRECTORYENTRY_H
#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/DenseMapInfo.h"
+#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ErrorOr.h"
StringRef getName() const { return ME->getKey(); }
+ /// Hash code is based on the DirectoryEntry, not the specific named
+ /// reference.
+ friend llvm::hash_code hash_value(DirectoryEntryRef Ref) {
+ return llvm::hash_value(&Ref.getDirEntry());
+ }
+
using MapEntry = llvm::StringMapEntry<llvm::ErrorOr<DirectoryEntry &>>;
const MapEntry &getMapEntry() const { return *ME; }
+ /// Check if RHS referenced the file in exactly the same way.
+ bool isSameRef(DirectoryEntryRef RHS) const { return ME == RHS.ME; }
+
DirectoryEntryRef() = delete;
DirectoryEntryRef(const MapEntry &ME) : ME(&ME) {}
DirectoryEntryRef(optional_none_tag) : ME(nullptr) {}
bool hasOptionalValue() const { return ME; }
+ friend struct llvm::DenseMapInfo<DirectoryEntryRef>;
+ struct dense_map_empty_tag {};
+ struct dense_map_tombstone_tag {};
+
+ // Private constructors for use by DenseMapInfo.
+ DirectoryEntryRef(dense_map_empty_tag)
+ : ME(llvm::DenseMapInfo<const MapEntry *>::getEmptyKey()) {}
+ DirectoryEntryRef(dense_map_tombstone_tag)
+ : ME(llvm::DenseMapInfo<const MapEntry *>::getTombstoneKey()) {}
+ bool isSpecialDenseMapKey() const {
+ return isSameRef(DirectoryEntryRef(dense_map_empty_tag())) ||
+ isSameRef(DirectoryEntryRef(dense_map_tombstone_tag()));
+ }
+
const MapEntry *ME;
};
"Optional<DirectoryEntryRef> should be trivially copyable");
} // end namespace optional_detail
+
+/// Specialisation of DenseMapInfo for DirectoryEntryRef.
+template <> struct DenseMapInfo<clang::DirectoryEntryRef> {
+ static inline clang::DirectoryEntryRef getEmptyKey() {
+ return clang::DirectoryEntryRef(
+ clang::DirectoryEntryRef::dense_map_empty_tag());
+ }
+
+ static inline clang::DirectoryEntryRef getTombstoneKey() {
+ return clang::DirectoryEntryRef(
+ clang::DirectoryEntryRef::dense_map_tombstone_tag());
+ }
+
+ static unsigned getHashValue(clang::DirectoryEntryRef Val) {
+ return hash_value(Val);
+ }
+
+ static bool isEqual(clang::DirectoryEntryRef LHS,
+ clang::DirectoryEntryRef RHS) {
+ // Catch the easy cases: both empty, both tombstone, or the same ref.
+ if (LHS.isSameRef(RHS))
+ return true;
+
+ // Confirm LHS and RHS are valid.
+ if (LHS.isSpecialDenseMapKey() || RHS.isSpecialDenseMapKey())
+ return false;
+
+ // It's safe to use operator==.
+ return LHS == RHS;
+ }
+};
+
} // end namespace llvm
namespace clang {
#include "clang/Basic/DirectoryEntry.h"
#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/DenseMapInfo.h"
+#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
return !(LHS == RHS);
}
+ /// Hash code is based on the FileEntry, not the specific named reference,
+ /// just like operator==.
+ friend llvm::hash_code hash_value(FileEntryRef Ref) {
+ return llvm::hash_value(&Ref.getFileEntry());
+ }
+
struct MapValue;
/// Type used in the StringMap.
FileEntryRef(optional_none_tag) : ME(nullptr) {}
bool hasOptionalValue() const { return ME; }
+ friend struct llvm::DenseMapInfo<FileEntryRef>;
+ struct dense_map_empty_tag {};
+ struct dense_map_tombstone_tag {};
+
+ // Private constructors for use by DenseMapInfo.
+ FileEntryRef(dense_map_empty_tag)
+ : ME(llvm::DenseMapInfo<const MapEntry *>::getEmptyKey()) {}
+ FileEntryRef(dense_map_tombstone_tag)
+ : ME(llvm::DenseMapInfo<const MapEntry *>::getTombstoneKey()) {}
+ bool isSpecialDenseMapKey() const {
+ return isSameRef(FileEntryRef(dense_map_empty_tag())) ||
+ isSameRef(FileEntryRef(dense_map_tombstone_tag()));
+ }
+
const MapEntry *ME;
};
"Optional<FileEntryRef> should be trivially copyable");
} // end namespace optional_detail
+
+/// Specialisation of DenseMapInfo for FileEntryRef.
+template <> struct DenseMapInfo<clang::FileEntryRef> {
+ static inline clang::FileEntryRef getEmptyKey() {
+ return clang::FileEntryRef(clang::FileEntryRef::dense_map_empty_tag());
+ }
+
+ static inline clang::FileEntryRef getTombstoneKey() {
+ return clang::FileEntryRef(clang::FileEntryRef::dense_map_tombstone_tag());
+ }
+
+ static unsigned getHashValue(clang::FileEntryRef Val) {
+ return hash_value(Val);
+ }
+
+ static bool isEqual(clang::FileEntryRef LHS, clang::FileEntryRef RHS) {
+ // Catch the easy cases: both empty, both tombstone, or the same ref.
+ if (LHS.isSameRef(RHS))
+ return true;
+
+ // Confirm LHS and RHS are valid.
+ if (LHS.isSpecialDenseMapKey() || RHS.isSpecialDenseMapKey())
+ return false;
+
+ // It's safe to use operator==.
+ return LHS == RHS;
+ }
+};
+
} // end namespace llvm
namespace clang {
//===----------------------------------------------------------------------===//
#include "clang/Basic/FileEntry.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/StringMap.h"
#include "gtest/gtest.h"
FileMap Files;
DirMap Dirs;
- DirectoryEntry D;
- DirectoryEntryRef DR;
SmallVector<std::unique_ptr<FileEntry>, 5> FEs;
+ SmallVector<std::unique_ptr<DirectoryEntry>, 5> DEs;
+ DirectoryEntryRef DR;
- RefMaps() : DR(*Dirs.insert({"dir", D}).first) {}
+ RefMaps() : DR(addDirectory("dir")) {}
+
+ DirectoryEntryRef addDirectory(StringRef Name) {
+ DEs.push_back(std::make_unique<DirectoryEntry>());
+ return DirectoryEntryRef(*Dirs.insert({Name, *DEs.back()}).first);
+ }
+ DirectoryEntryRef addDirectoryAlias(StringRef Name, DirectoryEntryRef Base) {
+ return DirectoryEntryRef(
+ *Dirs.insert({Name, const_cast<DirectoryEntry &>(Base.getDirEntry())})
+ .first);
+ }
FileEntryRef addFile(StringRef Name) {
FEs.push_back(std::make_unique<FileEntry>());
EXPECT_FALSE(R1.isSameRef(R1Also));
}
+TEST(FileEntryTest, DenseMapInfo) {
+ RefMaps Refs;
+ FileEntryRef R1 = Refs.addFile("1");
+ FileEntryRef R2 = Refs.addFile("2");
+ FileEntryRef R1Also = Refs.addFileAlias("1-also", R1);
+
+ // Insert R1Also first and confirm it "wins".
+ {
+ SmallDenseSet<FileEntryRef, 8> Set;
+ Set.insert(R1Also);
+ Set.insert(R1);
+ Set.insert(R2);
+ EXPECT_TRUE(Set.find(R1Also)->isSameRef(R1Also));
+ EXPECT_TRUE(Set.find(R1)->isSameRef(R1Also));
+ EXPECT_TRUE(Set.find(R2)->isSameRef(R2));
+ }
+
+ // Insert R1Also second and confirm R1 "wins".
+ {
+ SmallDenseSet<FileEntryRef, 8> Set;
+ Set.insert(R1);
+ Set.insert(R1Also);
+ Set.insert(R2);
+ EXPECT_TRUE(Set.find(R1Also)->isSameRef(R1));
+ EXPECT_TRUE(Set.find(R1)->isSameRef(R1));
+ EXPECT_TRUE(Set.find(R2)->isSameRef(R2));
+ }
+}
+
+TEST(DirectoryEntryTest, isSameRef) {
+ RefMaps Refs;
+ DirectoryEntryRef R1 = Refs.addDirectory("1");
+ DirectoryEntryRef R2 = Refs.addDirectory("2");
+ DirectoryEntryRef R1Also = Refs.addDirectoryAlias("1-also", R1);
+
+ EXPECT_TRUE(R1.isSameRef(DirectoryEntryRef(R1)));
+ EXPECT_TRUE(R1.isSameRef(DirectoryEntryRef(R1.getMapEntry())));
+ EXPECT_FALSE(R1.isSameRef(R2));
+ EXPECT_FALSE(R1.isSameRef(R1Also));
+}
+
+TEST(DirectoryEntryTest, DenseMapInfo) {
+ RefMaps Refs;
+ DirectoryEntryRef R1 = Refs.addDirectory("1");
+ DirectoryEntryRef R2 = Refs.addDirectory("2");
+ DirectoryEntryRef R1Also = Refs.addDirectoryAlias("1-also", R1);
+
+ // Insert R1Also first and confirm it "wins".
+ {
+ SmallDenseSet<DirectoryEntryRef, 8> Set;
+ Set.insert(R1Also);
+ Set.insert(R1);
+ Set.insert(R2);
+ EXPECT_TRUE(Set.find(R1Also)->isSameRef(R1Also));
+ EXPECT_TRUE(Set.find(R1)->isSameRef(R1Also));
+ EXPECT_TRUE(Set.find(R2)->isSameRef(R2));
+ }
+
+ // Insert R1Also second and confirm R1 "wins".
+ {
+ SmallDenseSet<DirectoryEntryRef, 8> Set;
+ Set.insert(R1);
+ Set.insert(R1Also);
+ Set.insert(R2);
+ EXPECT_TRUE(Set.find(R1Also)->isSameRef(R1));
+ EXPECT_TRUE(Set.find(R1)->isSameRef(R1));
+ EXPECT_TRUE(Set.find(R2)->isSameRef(R2));
+ }
+}
+
} // end namespace