root/ppapi/tests/clang/print_names_and_sizes.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. HasArchDependentSize
  2. PrintTypeInfo
  3. HandleTopLevelDecl
  4. CreateASTConsumer
  5. ParseArgs

// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Clang plugin which prints the names and sizes of all the top-level
// structs, enums, and typedefs in the input file.

#include <cstdio>
#include <cstring>
#include <string>

#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/Decl.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendPluginRegistry.h"

namespace {

const char* const kTypedefName = "Typedef";
const char* const kDelim = ",";
const char* const kArchDependent = "ArchDependentSize";
const char* const kNotArchDependent = "NotArchDependentSize";

// This class consumes a Clang-parsed AST and prints out information about types
// defined in the global namespace.  Specifically, for each type definition
// encountered, it prints:
// "kind,name,size,arch_dependent,source_file,first_line,last_line\n"
// Where:
// - kind:  The Clang TypeClassName (Record, Enum, Typedef, Union, etc)
// - name:  The unmangled string name of the type.
// - size:  The size in bytes of the type.
// - arch_dependent:  'ArchDependentSize' if the type has architecture-dependent
//                    size, NotArchDependentSize otherwise.
// - source_file:  The source file in which the type is defined.
// - first_line:  The first line of the definition (counting from 0).
// - last_line:  The last line of the definition (counting from 0).
class PrintNamesAndSizesConsumer : public clang::ASTConsumer {
 public:
  explicit PrintNamesAndSizesConsumer(clang::SourceManager* source_mgr)
      : source_manager_(source_mgr) {}

 private:
  // SourceManager has information about source code locations, to help us know
  // where definitions appear.  We do not own this pointer, so we must not
  // delete it.
  clang::SourceManager* source_manager_;

  // Return true if the type contains types that differ in size between 32-bit
  // and 64-bit architectures.  This is true for types that are typedefed to a
  // pointer, long, or unsigned long and also any types that contain an
  // architecture-dependent type.  Note that this is a bit overly restrictive;
  // some unions may be consistent size on 32-bit and 64-bit architectures
  // despite containing one of these types.  But it's not an important enough
  // issue to warrant coding the special case.
  // Structs, enums, and unions that do NOT contain arch-dependent types are
  // crafted to be the same size on 32-bit and 64-bit platforms by convention.
  bool HasArchDependentSize(const clang::Type& type) {
    if (type.isPointerType()) {
      return true;
    } else if (const clang::BuiltinType* builtin =
        dyn_cast<clang::BuiltinType>(&type)) {
      if ((builtin->getKind() == clang::BuiltinType::Long) ||
          (builtin->getKind() == clang::BuiltinType::ULong)) {
        return true;
      }
    } else if (const clang::ArrayType* array =
        dyn_cast<clang::ArrayType>(&type)) {
      // If it's an array, it has architecture-dependent size if its elements
      // do.
      return HasArchDependentSize(*(array->getElementType().getTypePtr()));
    } else if (const clang::TypedefType* typedef_type =
        dyn_cast<clang::TypedefType>(&type)) {
      return HasArchDependentSize(*(typedef_type->desugar().getTypePtr()));
    } else if (const clang::RecordType* record =
        dyn_cast<clang::RecordType>(&type)) {
      // If it's a struct or union, iterate through the fields.  If any of them
      // has architecture-dependent size, then we do too.
      const clang::RecordDecl* decl = record->getDecl();
      clang::RecordDecl::field_iterator iter(decl->field_begin());
      clang::RecordDecl::field_iterator end(decl->field_end());
      for (; iter != end; ++iter) {
        if (HasArchDependentSize(*(iter->getType().getTypePtr()))) {
          return true;
        }
      }
      // It's a struct or union, but contains no architecture-dependent members.
      return false;
    }
    return false;
  }

  void PrintTypeInfo(const std::string& name, const clang::Type& type,
                     const std::string& kind, const clang::CharUnits& size,
                     const clang::SourceLocation& begin_loc,
                     const clang::SourceLocation& end_loc) {
    clang::PresumedLoc presumed_begin(
        source_manager_->getPresumedLoc(begin_loc));
    clang::PresumedLoc presumed_end(source_manager_->getPresumedLoc(end_loc));
    std::printf("%s,%s,%lu,%s,%s,%u,%u\n",
                kind.c_str(),
                name.c_str(),
                size.getQuantity(),
                HasArchDependentSize(type) ? kArchDependent : kNotArchDependent,
                presumed_begin.getFilename(),
                presumed_begin.getLine(),
                presumed_end.getLine());
  }

  // Virtual function to consume top-level declarations.  For each one, we check
  // to see if it is a type definition.  If it is, we print information about
  // it.
  virtual void HandleTopLevelDecl(clang::DeclGroupRef decl_group) {
    clang::DeclGroupRef::iterator iter(decl_group.begin());
    clang::DeclGroupRef::iterator the_end(decl_group.end());
    for (; iter != the_end; ++iter) {
      const clang::Decl *decl = *iter;
      if (const clang::TypeDecl* type_decl = dyn_cast<clang::TypeDecl>(decl)) {
        std::string name(type_decl->getNameAsString());
        clang::SourceLocation start_loc = type_decl->getLocStart();
        clang::SourceLocation end_loc = type_decl->getLocEnd();
        // TagDecl covers structs, enums, unions, and classes.
        if (const clang::TagDecl* tag = dyn_cast<clang::TagDecl>(type_decl)) {
          // Only print out info when we find the definition;  ignore forward
          // references.
          if (tag->isDefinition()) {
            clang::Type* type = type_decl->getTypeForDecl();
            clang::CharUnits size =
                tag->getASTContext().getTypeSizeInChars(type);
            PrintTypeInfo(name, *type, type->getTypeClassName(), size,
                          start_loc, end_loc);
          }
        } else if (const clang::TypedefDecl* td =
                   dyn_cast<clang::TypedefDecl>(type_decl)) {
          clang::Type* type = td->getUnderlyingType().getTypePtr();
          clang::CharUnits size = td->getASTContext().getTypeSizeInChars(type);
          PrintTypeInfo(name, *type, kTypedefName, size, start_loc, end_loc);
        }
      }
    }
  }
};

class PrintNamesAndSizesAction : public clang::PluginASTAction {
 public:
  PrintNamesAndSizesAction() {}

 private:
  virtual clang::ASTConsumer *CreateASTConsumer(
      clang::CompilerInstance &instance, llvm::StringRef /*input_file*/) {
    return new PrintNamesAndSizesConsumer(
        &(instance.getDiagnostics().getSourceManager()));
  }

  // We don't accept any arguments, but ParseArgs is pure-virtual.
  virtual bool ParseArgs(const clang::CompilerInstance& /*instance*/,
                         const std::vector<std::string>& /*args*/) {
    return true;
  }
};

}  // namespace

static clang::FrontendPluginRegistry::Add<PrintNamesAndSizesAction>
X("PrintNamesAndSizes",
  "Print the names and sizes of classes, enums, and typedefs.");


/* [<][>][^][v][top][bottom][index][help] */