Home | History | Annotate | Download | only in clang
      1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Clang plugin which prints the names and sizes of all the top-level
      6 // structs, enums, and typedefs in the input file.
      7 
      8 #include <cstdio>
      9 #include <cstring>
     10 #include <string>
     11 
     12 #include "clang/AST/AST.h"
     13 #include "clang/AST/ASTConsumer.h"
     14 #include "clang/AST/CharUnits.h"
     15 #include "clang/AST/Decl.h"
     16 #include "clang/Basic/SourceManager.h"
     17 #include "clang/Basic/TargetInfo.h"
     18 #include "clang/Frontend/CompilerInstance.h"
     19 #include "clang/Frontend/FrontendPluginRegistry.h"
     20 
     21 namespace {
     22 
     23 const char* const kTypedefName = "Typedef";
     24 const char* const kDelim = ",";
     25 const char* const kArchDependent = "ArchDependentSize";
     26 const char* const kNotArchDependent = "NotArchDependentSize";
     27 
     28 // This class consumes a Clang-parsed AST and prints out information about types
     29 // defined in the global namespace.  Specifically, for each type definition
     30 // encountered, it prints:
     31 // "kind,name,size,arch_dependent,source_file,first_line,last_line\n"
     32 // Where:
     33 // - kind:  The Clang TypeClassName (Record, Enum, Typedef, Union, etc)
     34 // - name:  The unmangled string name of the type.
     35 // - size:  The size in bytes of the type.
     36 // - arch_dependent:  'ArchDependentSize' if the type has architecture-dependent
     37 //                    size, NotArchDependentSize otherwise.
     38 // - source_file:  The source file in which the type is defined.
     39 // - first_line:  The first line of the definition (counting from 0).
     40 // - last_line:  The last line of the definition (counting from 0).
     41 class PrintNamesAndSizesConsumer : public clang::ASTConsumer {
     42  public:
     43   explicit PrintNamesAndSizesConsumer(clang::SourceManager* source_mgr)
     44       : source_manager_(source_mgr) {}
     45 
     46  private:
     47   // SourceManager has information about source code locations, to help us know
     48   // where definitions appear.  We do not own this pointer, so we must not
     49   // delete it.
     50   clang::SourceManager* source_manager_;
     51 
     52   // Return true if the type contains types that differ in size between 32-bit
     53   // and 64-bit architectures.  This is true for types that are typedefed to a
     54   // pointer, long, or unsigned long and also any types that contain an
     55   // architecture-dependent type.  Note that this is a bit overly restrictive;
     56   // some unions may be consistent size on 32-bit and 64-bit architectures
     57   // despite containing one of these types.  But it's not an important enough
     58   // issue to warrant coding the special case.
     59   // Structs, enums, and unions that do NOT contain arch-dependent types are
     60   // crafted to be the same size on 32-bit and 64-bit platforms by convention.
     61   bool HasArchDependentSize(const clang::Type& type) {
     62     if (type.isPointerType()) {
     63       return true;
     64     } else if (const clang::BuiltinType* builtin =
     65         dyn_cast<clang::BuiltinType>(&type)) {
     66       if ((builtin->getKind() == clang::BuiltinType::Long) ||
     67           (builtin->getKind() == clang::BuiltinType::ULong)) {
     68         return true;
     69       }
     70     } else if (const clang::ArrayType* array =
     71         dyn_cast<clang::ArrayType>(&type)) {
     72       // If it's an array, it has architecture-dependent size if its elements
     73       // do.
     74       return HasArchDependentSize(*(array->getElementType().getTypePtr()));
     75     } else if (const clang::TypedefType* typedef_type =
     76         dyn_cast<clang::TypedefType>(&type)) {
     77       return HasArchDependentSize(*(typedef_type->desugar().getTypePtr()));
     78     } else if (const clang::RecordType* record =
     79         dyn_cast<clang::RecordType>(&type)) {
     80       // If it's a struct or union, iterate through the fields.  If any of them
     81       // has architecture-dependent size, then we do too.
     82       const clang::RecordDecl* decl = record->getDecl();
     83       clang::RecordDecl::field_iterator iter(decl->field_begin());
     84       clang::RecordDecl::field_iterator end(decl->field_end());
     85       for (; iter != end; ++iter) {
     86         if (HasArchDependentSize(*(iter->getType().getTypePtr()))) {
     87           return true;
     88         }
     89       }
     90       // It's a struct or union, but contains no architecture-dependent members.
     91       return false;
     92     }
     93     return false;
     94   }
     95 
     96   void PrintTypeInfo(const std::string& name, const clang::Type& type,
     97                      const std::string& kind, const clang::CharUnits& size,
     98                      const clang::SourceLocation& begin_loc,
     99                      const clang::SourceLocation& end_loc) {
    100     clang::PresumedLoc presumed_begin(
    101         source_manager_->getPresumedLoc(begin_loc));
    102     clang::PresumedLoc presumed_end(source_manager_->getPresumedLoc(end_loc));
    103     std::printf("%s,%s,%lu,%s,%s,%u,%u\n",
    104                 kind.c_str(),
    105                 name.c_str(),
    106                 size.getQuantity(),
    107                 HasArchDependentSize(type) ? kArchDependent : kNotArchDependent,
    108                 presumed_begin.getFilename(),
    109                 presumed_begin.getLine(),
    110                 presumed_end.getLine());
    111   }
    112 
    113   // Virtual function to consume top-level declarations.  For each one, we check
    114   // to see if it is a type definition.  If it is, we print information about
    115   // it.
    116   virtual void HandleTopLevelDecl(clang::DeclGroupRef decl_group) {
    117     clang::DeclGroupRef::iterator iter(decl_group.begin());
    118     clang::DeclGroupRef::iterator the_end(decl_group.end());
    119     for (; iter != the_end; ++iter) {
    120       const clang::Decl *decl = *iter;
    121       if (const clang::TypeDecl* type_decl = dyn_cast<clang::TypeDecl>(decl)) {
    122         std::string name(type_decl->getNameAsString());
    123         clang::SourceLocation start_loc = type_decl->getLocStart();
    124         clang::SourceLocation end_loc = type_decl->getLocEnd();
    125         // TagDecl covers structs, enums, unions, and classes.
    126         if (const clang::TagDecl* tag = dyn_cast<clang::TagDecl>(type_decl)) {
    127           // Only print out info when we find the definition;  ignore forward
    128           // references.
    129           if (tag->isDefinition()) {
    130             clang::Type* type = type_decl->getTypeForDecl();
    131             clang::CharUnits size =
    132                 tag->getASTContext().getTypeSizeInChars(type);
    133             PrintTypeInfo(name, *type, type->getTypeClassName(), size,
    134                           start_loc, end_loc);
    135           }
    136         } else if (const clang::TypedefDecl* td =
    137                    dyn_cast<clang::TypedefDecl>(type_decl)) {
    138           clang::Type* type = td->getUnderlyingType().getTypePtr();
    139           clang::CharUnits size = td->getASTContext().getTypeSizeInChars(type);
    140           PrintTypeInfo(name, *type, kTypedefName, size, start_loc, end_loc);
    141         }
    142       }
    143     }
    144   }
    145 };
    146 
    147 class PrintNamesAndSizesAction : public clang::PluginASTAction {
    148  public:
    149   PrintNamesAndSizesAction() {}
    150 
    151  private:
    152   virtual clang::ASTConsumer *CreateASTConsumer(
    153       clang::CompilerInstance &instance, llvm::StringRef /*input_file*/) {
    154     return new PrintNamesAndSizesConsumer(
    155         &(instance.getDiagnostics().getSourceManager()));
    156   }
    157 
    158   // We don't accept any arguments, but ParseArgs is pure-virtual.
    159   virtual bool ParseArgs(const clang::CompilerInstance& /*instance*/,
    160                          const std::vector<std::string>& /*args*/) {
    161     return true;
    162   }
    163 };
    164 
    165 }  // namespace
    166 
    167 static clang::FrontendPluginRegistry::Add<PrintNamesAndSizesAction>
    168 X("PrintNamesAndSizes",
    169   "Print the names and sizes of classes, enums, and typedefs.");
    170 
    171