Movatterモバイル変換


[0]ホーム

URL:


Google Git
Sign in
chromium /chromium /src /main /. /tools /clang /plugins /TypePredicateUtil.h
blob: 85782641f5ba7df98112aa54f3d6caef88699293 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TOOLS_CLANG_PLUGINS_TYPEPREDICATEUTIL_H_
#define TOOLS_CLANG_PLUGINS_TYPEPREDICATEUTIL_H_
#include<map>
#include<memory>
#include<optional>
#include<set>
#include<string>
#include<vector>
#include"clang/AST/Decl.h"
#include"clang/AST/DeclTemplate.h"
#include"clang/AST/Type.h"
#include"llvm/ADT/ScopeExit.h"
enumclassInductionRule:unsigned{
kNone=0,
kPointerPointee=(1<<0),
kObjCPointerPointee=(1<<1),
kReferencePointee=(1<<2),
kArrayElement=(1<<3),
kUnqualifiedDesugaredType=(1<<4),
kBaseClass=(1<<5),
kVirtualBaseClass=(1<<6),
kField=(1<<7),
kTemplateArgument=(1<<8),
};
constexprInductionRuleoperator|(InductionRule a,InductionRule b){
returnstatic_cast<InductionRule>(static_cast<unsigned>(a)|
static_cast<unsigned>(b));
}
constexprInductionRuleoperator&(InductionRule a,InductionRule b){
returnstatic_cast<InductionRule>(static_cast<unsigned>(a)&
static_cast<unsigned>(b));
}
// Represents a match result |verdict_|.
// - MatchResult::kNoMatch: no match found against |type|.
// - MatchResult::kMatch: a match found against |type|.
// - MatchResult::kUndetermined: This denotes the result
// is not yet determined, due to cross references.
// Holds some additional information to tell reasons.
classMatchResult{
public:
enumVerdict{
kMatch,
kNoMatch,
// This denotes the match status is not yet determined.
kUndetermined,
};
explicitMatchResult(const clang::Type* type): type_(type){}
explicitMatchResult(const clang::Type* type,Verdict verdict)
: type_(type), verdict_(verdict){}
const clang::Type* type()const{return type_;}
Verdict verdict()const{returnthis->verdict_;}
std::shared_ptr<MatchResult> source()const{returnthis->source_;}
std::optional<clang::SourceLocation> source_loc()const{
returnthis->source_loc_;
}
private:
template<InductionRuleRules>
friendclassTypePredicate;
// Merges a sub verdict into this type's verdict.
//
// | this \ sub | kNoMatch | kUndetermined | kMatch |
// +---------------+---------------+---------------+--------+
// | kNoMatch | kNoMatch | kUndetermined | kMatch |
// | kUndetermined | kUndetermined | kUndetermined | kMatch |
// | kMatch | kMatch | kMatch | kMatch |
VerdictMergeSubResult(
std::shared_ptr<MatchResult>sub,
std::optional<clang::SourceLocation> loc= std::nullopt){
if(sub->verdict_== kMatch&&this->verdict_!= kMatch){
this->verdict_= kMatch;
this->source_= std::move(sub);
this->source_loc_= loc;
}elseif(sub->verdict_== kUndetermined&&this->verdict_== kNoMatch){
this->verdict_= kUndetermined;
this->source_= std::move(sub);
this->source_loc_= loc;
}
returnthis->verdict_;
}
// |type_| is considered to be |verdict_|.
// Optionally, the result contains a reason for the verdict, |source_|.
// There can be multiple reasons (e.g. |type_| has multiple matching
// members), but only one of them is stored. The relation between |type_|
// and |source_| is optionally shown at |source_loc_|.
const clang::Type* type_;
Verdict verdict_= kNoMatch;
std::shared_ptr<MatchResult> source_;
std::optional<clang::SourceLocation> source_loc_;
};
// Determines there is a match against |type| or not.
// A type is considered match if |IsBaseMatch| returns true or
// reach such |type| by applying InductionRule recursively.
template<InductionRuleRules>
classTypePredicate{
public:
virtual~TypePredicate()=default;
boolMatches(const clang::Type* type)const{
returnGetMatchResult(type)->verdict_==MatchResult::kMatch;
}
std::shared_ptr<MatchResult>GetMatchResult(
const clang::Type* type,
std::set<const clang::Type*>* visited=nullptr)const{
// Retrieve a "base" type to reduce recursion depth.
const clang::Type* raw_type=GetBaseType(type);
if(!raw_type||!raw_type->isRecordType()){
// |TypePredicate| does not support followings:
// - function type
// - enum type
// - builtin type
// - complex type
// - obj-C types
// - using type
// - typeof type
return std::make_shared<MatchResult>(type);// No match.
}
// Use a memoized result if exists.
auto iter= cache_.find(type);
if(iter!= cache_.end()){
return iter->second;
}
// This performs DFS on a directed graph composed of |Type*|.
// Avoid searching for visited nodes by managing |visited|, as this can lead
// to infinite loops in the presence of self-references and
// cross-references. Since finding a match for |Type* x| is equivalent to
// being able to reach from node |Type* x| to node |Type* y| where
// |IsBaseCase(y)|, there is no need to look up visited nodes again.
bool root= visited==nullptr;
if(root){
// Will be deleted as a part of |clean_up()|.
visited=new std::set<const clang::Type*>();
}elseif(visited->count(type)){
// This type is already visited but not memoized,
// therefore this node is reached by following cross-references from
// ancestors. The verdict of this node cannot be determined without
// waiting for computation in its ancestors.
return std::make_shared<MatchResult>(raw_type,
MatchResult::kUndetermined);
}
visited->insert(type);
auto match= std::make_shared<MatchResult>(raw_type);
// Clean-up: this lambda is called automatically at the scope exit.
constauto clean_up=
llvm::make_scope_exit([this,&visited,&raw_type,&root,&match]{
if(root){
delete visited;
}
// Memoize the result if finalized.
if(match->verdict_!=MatchResult::kUndetermined){
this->cache_.insert({raw_type, match});
}
});
// Base case.
if(IsBaseMatch(raw_type)){
match->verdict_=MatchResult::kMatch;
return match;
}
const clang::RecordDecl* decl= raw_type->getAsRecordDecl();
assert(decl);
// Check member fields
ifconstexpr((Rules&InductionRule::kField)!=InductionRule::kNone){
for(constauto& field: decl->fields()){
match->MergeSubResult(
GetMatchResult(field->getType().getTypePtrOrNull(), visited),
field->getBeginLoc());
// Verdict finalized: early return.
if(match->verdict_==MatchResult::kMatch){
return match;
}
}
}
constauto* cxx_decl= clang::dyn_cast<clang::CXXRecordDecl>(decl);
if(cxx_decl&& cxx_decl->hasDefinition()){
// Check base classes
ifconstexpr((Rules&InductionRule::kBaseClass)!=
InductionRule::kNone){
for(constauto& base_specifier: cxx_decl->bases()){
match->MergeSubResult(
GetMatchResult(base_specifier.getType().getTypePtr(), visited),
base_specifier.getBeginLoc());
// Verdict finalized: early return.
if(match->verdict_==MatchResult::kMatch){
return match;
}
}
}
// Check virtual base classes
ifconstexpr((Rules&InductionRule::kVirtualBaseClass)!=
InductionRule::kNone){
for(constauto& base_specifier: cxx_decl->vbases()){
match->MergeSubResult(
GetMatchResult(base_specifier.getType().getTypePtr(), visited),
base_specifier.getBeginLoc());
// Verdict finalized: early return.
if(match->verdict_==MatchResult::kMatch){
return match;
}
}
}
}
// Check template parameters.
ifconstexpr((Rules&InductionRule::kTemplateArgument)!=
InductionRule::kNone){
if(auto* field_record_template=
clang::dyn_cast<clang::ClassTemplateSpecializationDecl>(decl)){
constauto& template_args= field_record_template->getTemplateArgs();
for(unsigned i=0; i< template_args.size(); i++){
if(template_args[i].getKind()!= clang::TemplateArgument::Type){
continue;
}
match->MergeSubResult(
GetMatchResult(template_args[i].getAsType().getTypePtrOrNull(),
visited),
field_record_template->getTemplateKeywordLoc());
// Verdict finalized: early return.
if(match->verdict_==MatchResult::kMatch){
return match;
}
}
}
}
// All reachable types have been traversed but the root type has not
// been marked as a match; therefore it must be no match.
if(root&& match->verdict_==MatchResult::kUndetermined){
match->verdict_=MatchResult::kNoMatch;
}
return match;
}
private:
const clang::Type*GetBaseType(const clang::Type* type)const{
using clang::dyn_cast;
const clang::Type* last_type=nullptr;
while(type&& type!= last_type){
last_type= type;
// Unwrap type aliases.
ifconstexpr((Rules&InductionRule::kUnqualifiedDesugaredType)!=
InductionRule::kNone){
type= type->getUnqualifiedDesugaredType();
}
// Unwrap pointers.
ifconstexpr((Rules&InductionRule::kPointerPointee)!=
InductionRule::kNone){
while(type&& type->isPointerType()){
type= type->getPointeeType().getTypePtr();
}
}
// Unwrap ObjC pointers.
ifconstexpr((Rules&InductionRule::kObjCPointerPointee)!=
InductionRule::kNone){
while(type&& type->isObjCObjectPointerType()){
type= type->getPointeeType().getTypePtr();
}
}
// Unwrap array.
ifconstexpr((Rules&InductionRule::kArrayElement)!=
InductionRule::kNone){
while(constauto* array_type= dyn_cast<clang::ArrayType>(type)){
type= array_type->getElementType().getTypePtr();
}
}
// Unwrap reference.
ifconstexpr((Rules&InductionRule::kReferencePointee)!=
InductionRule::kNone){
if(constauto* ref_type= dyn_cast<clang::ReferenceType>(type)){
type= ref_type->getPointeeType().getTypePtrOrNull();
}
}
}
return type;
}
virtualboolIsBaseMatch(const clang::Type* type)const{returnfalse;}
// Cache to efficiently determine match.
mutable std::map<const clang::Type*, std::shared_ptr<MatchResult>> cache_;
};
#endif// TOOLS_CLANG_PLUGINS_TYPEPREDICATEUTIL_H_

[8]ページ先頭

©2009-2025 Movatter.jp