//===- ExtractAPI/Serialization/APISetVisitor.h ----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// This file defines the ExtractAPI APISetVisitor interface. /// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SERIALIZERBASE_H #define LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SERIALIZERBASE_H #include "clang/ExtractAPI/API.h" namespace clang { namespace extractapi { // A helper macro to implement short-circuiting when recursing. It // invokes CALL_EXPR, which must be a method call, on the derived // object (s.t. a user of RecursiveASTVisitor can override the method // in CALL_EXPR). #define TRY_TO(CALL_EXPR) \ do { \ if (!getDerived()->CALL_EXPR) \ return false; \ } while (false) /// The base interface of visitors for API information, the interface and usage /// is almost identical to RecurisveASTVistor. This class performs three /// distinct tasks: /// 1. traverse the APISet (i.e. go to every record); /// 2. at a given record, walk up the class hierarchy starting from the record's /// dynamic type until APIRecord is reached. /// 3. given a (record, class) combination where 'class' is some base class of /// the dynamic type of 'record', call a user-overridable function to actually /// visit the record. /// /// These tasks are done by three groups of methods, respectively: /// 1. traverseRecord(APIRecord *x) does task #1, it is the entry point for /// traversing the records starting from x. This method simply forwards to /// traverseFoo(Foo *x) where Foo is the dynamic type of *x, which calls /// walkUpFromFoo(x) and then recursively visits the child records of x. /// 2. walkUpFromFoo(Foo *x) does task #2. It doesn't visit children records of /// x, instead it first calls walkUpFromBar(x) where Bar is the direct parent /// class of Foo (unless Foo has no parent) and then calls visitFoo(x). /// 3. visitFoo(Foo *x) does task #3. /// /// These three method groups are tiered (traverse* > walkUpFrom* > /// visit*). A method (e.g. traverse*) may call methods from the same /// tier (e.g. other traverse*) or one tier lower (e.g. walkUpFrom*). /// It may not call methods from a higher tier. /// /// Note that since walkUpFromFoo() calls walkUpFromBar() (where Bar /// is Foo's super class) before calling visitFoo(), the result is /// that the visit*() methods for a given record are called in the /// top-down order (e.g. for a record of type ObjCInstancePropertyRecord, the /// order will be visitRecord(), visitObjCPropertyRecord(), and then /// visitObjCInstancePropertyRecord()). /// /// This scheme guarantees that all visit*() calls for the same record /// are grouped together. In other words, visit*() methods for different /// records are never interleaved. /// /// Clients of this visitor should subclass the visitor (providing /// themselves as the template argument, using the curiously recurring /// template pattern) and override any of the traverse*, walkUpFrom*, /// and visit* methods for records where the visitor should customize /// behavior. Most users only need to override visit*. Advanced /// users may override traverse* and walkUpFrom* to implement custom /// traversal strategies. Returning false from one of these overridden /// functions will abort the entire traversal. template class APISetVisitor { public: bool traverseAPISet() { for (const APIRecord *TLR : API.getTopLevelRecords()) { TRY_TO(traverseAPIRecord(TLR)); } return true; } bool traverseAPIRecord(const APIRecord *Record); bool walkUpFromAPIRecord(const APIRecord *Record) { TRY_TO(visitAPIRecord(Record)); return true; } bool visitAPIRecord(const APIRecord *Record) { return true; } #define GENERATE_TRAVERSE_METHOD(CLASS, BASE) \ bool traverse##CLASS(const CLASS *Record) { \ TRY_TO(walkUpFrom##CLASS(Record)); \ TRY_TO(traverseRecordContext(dyn_cast(Record))); \ return true; \ } #define GENERATE_WALKUP_AND_VISIT_METHODS(CLASS, BASE) \ bool walkUpFrom##CLASS(const CLASS *Record) { \ TRY_TO(walkUpFrom##BASE(Record)); \ TRY_TO(visit##CLASS(Record)); \ return true; \ } \ bool visit##CLASS(const CLASS *Record) { return true; } #define CONCRETE_RECORD(CLASS, BASE, KIND) \ GENERATE_TRAVERSE_METHOD(CLASS, BASE) \ GENERATE_WALKUP_AND_VISIT_METHODS(CLASS, BASE) #define ABSTRACT_RECORD(CLASS, BASE) \ GENERATE_WALKUP_AND_VISIT_METHODS(CLASS, BASE) #include "../APIRecords.inc" #undef GENERATE_WALKUP_AND_VISIT_METHODS #undef GENERATE_TRAVERSE_METHOD bool traverseRecordContext(const RecordContext *); protected: const APISet &API; public: APISetVisitor() = delete; APISetVisitor(const APISetVisitor &) = delete; APISetVisitor(APISetVisitor &&) = delete; APISetVisitor &operator=(const APISetVisitor &) = delete; APISetVisitor &operator=(APISetVisitor &&) = delete; protected: APISetVisitor(const APISet &API) : API(API) {} ~APISetVisitor() = default; Derived *getDerived() { return static_cast(this); }; }; template bool APISetVisitor::traverseRecordContext( const RecordContext *Context) { if (!Context) return true; for (auto *Child : Context->records()) TRY_TO(traverseAPIRecord(Child)); return true; } template bool APISetVisitor::traverseAPIRecord(const APIRecord *Record) { switch (Record->getKind()) { #define CONCRETE_RECORD(CLASS, BASE, KIND) \ case APIRecord::KIND: { \ TRY_TO(traverse##CLASS(static_cast(Record))); \ break; \ } #include "../APIRecords.inc" case APIRecord::RK_Unknown: { TRY_TO(walkUpFromAPIRecord(static_cast(Record))); break; } default: llvm_unreachable("API Record with uninstantiable kind"); } return true; } } // namespace extractapi } // namespace clang #endif // LLVM_CLANG_EXTRACTAPI_SERIALIZATION_SERIALIZERBASE_H