blob: c6b6ba6fdb559fb95947f7bc4fff88c537e12dd9 [file] [log] [blame]
/*
* Copyright 2018 The Kythe Authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "kythe/cxx/indexer/cxx/semantic_hash.h"
#include <string>
#include "glog/logging.h"
namespace kythe {
uint64_t SemanticHash::HashTemplateDeclish(const clang::Decl* decl) const {
return std::hash<std::string>()(decl_string_(decl));
}
uint64_t SemanticHash::Hash(const clang::TemplateName& name) const {
using ::clang::TemplateName;
switch (name.getKind()) {
case TemplateName::Template:
return HashTemplateDeclish(name.getAsTemplateDecl());
case TemplateName::OverloadedTemplate:
CHECK(ignore_unimplemented_) << "SemanticHash(OverloadedTemplate)";
return 0;
case TemplateName::QualifiedTemplate:
CHECK(ignore_unimplemented_) << "SemanticHash(QualifiedTemplate)";
return 0;
case TemplateName::DependentTemplate:
CHECK(ignore_unimplemented_) << "SemanticHash(DependentTemplate)";
return 0;
case TemplateName::SubstTemplateTemplateParm:
CHECK(ignore_unimplemented_) << "SemanticHash(SubstTemplateTemplateParm)";
return 0;
case TemplateName::SubstTemplateTemplateParmPack:
CHECK(ignore_unimplemented_)
<< "SemanticHash(SubstTemplateTemplateParmPack)";
return 0;
default:
LOG(FATAL) << "Unexpected TemplateName Kind";
}
return 0;
}
uint64_t SemanticHash::Hash(const clang::TemplateArgument& arg) const {
using ::clang::TemplateArgument;
switch (arg.getKind()) {
case TemplateArgument::Null:
return 0x1010101001010101LL; // Arbitrary constant for H(Null).
case TemplateArgument::Type:
return Hash(arg.getAsType()) ^ 0x2020202002020202LL;
case TemplateArgument::Declaration:
return Hash(arg.getParamTypeForDecl()) ^
std::hash<std::string>()(
arg.getAsDecl()->getQualifiedNameAsString());
case TemplateArgument::NullPtr:
return 0;
case TemplateArgument::Integral: {
auto value = arg.getAsIntegral();
if (value.getMinSignedBits() <= sizeof(uint64_t) * CHAR_BIT) {
return static_cast<uint64_t>(value.getExtValue());
} else {
return std::hash<std::string>()(value.toString(10));
}
}
case TemplateArgument::Template:
return Hash(arg.getAsTemplate()) ^ 0x4040404004040404LL;
case TemplateArgument::TemplateExpansion:
CHECK(ignore_unimplemented_) << "SemanticHash(TemplateExpansion)";
return 0;
case TemplateArgument::Expression:
CHECK(ignore_unimplemented_) << "SemanticHash(Expression)";
return 0;
case TemplateArgument::Pack: {
uint64_t out = 0x8080808008080808LL;
for (const auto& element : arg.pack_elements()) {
out = Hash(element) ^ ((out << 1) | (out >> 63));
}
return out;
}
default:
LOG(FATAL) << "Unexpected TemplateArgument Kind";
}
return 0;
}
uint64_t SemanticHash::Hash(const clang::QualType& type) const {
return std::hash<std::string>()(type.getCanonicalType().getAsString());
}
uint64_t SemanticHash::Hash(const clang::EnumDecl* decl) const {
// Memoize semantic hashes for enums, as they are also needed to create
// names for enum constants.
auto inserted = enum_cache_.insert({decl, 0});
if (!inserted.second) return inserted.first->second;
// TODO(zarko): Do we need a better hash function?
uint64_t hash = 0;
for (auto member : decl->enumerators()) {
if (member->getDeclName().isIdentifier()) {
hash ^= std::hash<std::string>()(member->getName());
}
}
inserted.first->second = hash;
return hash;
}
uint64_t SemanticHash::Hash(const clang::TemplateArgumentList* arg_list) const {
uint64_t hash = 0;
for (const auto& arg : arg_list->asArray()) {
hash ^= Hash(arg);
}
return hash;
}
uint64_t SemanticHash::Hash(const clang::RecordDecl* decl) const {
// TODO(zarko): Do we need a better hash function? We may need to
// hash the type variable context all the way up to the root template.
uint64_t hash = 0;
for (const auto* child : decl->decls()) {
if (child->getDeclContext() != decl) {
// Some decls appear underneath RD in the AST but aren't semantically
// part of it. For example, in
// struct S { struct T *t; };
// the RecordDecl for T is an AST child of S, but is a DeclContext
// sibling.
continue;
}
if (const auto* named_child = clang::dyn_cast<clang::NamedDecl>(child)) {
if (named_child->getDeclName().isIdentifier()) {
hash ^= std::hash<std::string>()(named_child->getName());
}
}
}
if (const auto* record_decl = clang::dyn_cast<clang::CXXRecordDecl>(decl)) {
if (const auto* tmpl_decl = record_decl->getDescribedClassTemplate()) {
hash ^= HashTemplateDeclish(tmpl_decl);
}
}
if (const auto* spec_decl =
clang::dyn_cast<clang::ClassTemplateSpecializationDecl>(decl)) {
hash ^= Hash(clang::QualType(spec_decl->getTypeForDecl(), 0));
}
return hash;
}
uint64_t SemanticHash::Hash(const clang::Selector& selector) const {
return std::hash<std::string>()(selector.getAsString());
}
} // namespace kythe