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.
5 // Clang plugin which finds types that are affected if the types listed in the
6 // plugin parameters are changed. This is for use in determining what PPAPI
7 // C-level interfaces are affected if one or more PPAPI C structs are changed.
16 #include "clang/AST/AST.h"
17 #include "clang/AST/ASTConsumer.h"
18 #include "clang/AST/CharUnits.h"
19 #include "clang/Basic/SourceManager.h"
20 #include "clang/Basic/TargetInfo.h"
21 #include "clang/Frontend/CompilerInstance.h"
22 #include "clang/Frontend/FrontendPluginRegistry.h"
26 typedef std::vector
<std::string
> StringVec
;
28 class FindAffectedInterfacesConsumer
: public clang::ASTConsumer
{
30 explicit FindAffectedInterfacesConsumer(const StringVec
& changed_classes
) {
31 StringVec::const_iterator
iter(changed_classes
.begin());
32 StringVec::const_iterator
the_end(changed_classes
.end());
33 for (; iter
!= the_end
; ++iter
) {
34 class_is_affected_map_
[*iter
] = true;
39 typedef std::map
<std::string
, bool> StringBoolMap
;
40 StringBoolMap class_is_affected_map_
;
42 bool IsAffected(const clang::Type
& type_to_check
) {
43 std::string
type_string(
44 type_to_check
.getCanonicalTypeInternal().getAsString());
45 std::pair
<StringBoolMap::iterator
, bool> iter_success_pair
=
46 class_is_affected_map_
.insert(
47 StringBoolMap::value_type(type_string
, false));
48 StringBoolMap::iterator
iter(iter_success_pair
.first
);
49 bool successfully_inserted(iter_success_pair
.second
);
50 // If we were able to insert, then we haven't yet seen this type. Compute
51 // IsAffected and put it at the newly inserted location in the map.
52 if (successfully_inserted
) {
53 if (type_to_check
.isPointerType()) {
54 const clang::PointerType
* pointer_type
=
55 dyn_cast
<clang::PointerType
>(&type_to_check
);
56 // Recurse to the pointee type.
57 iter
->second
= IsAffected(*pointer_type
->getPointeeType().getTypePtr());
58 } else if (type_to_check
.isFunctionProtoType()) {
59 const clang::FunctionProtoType
* func_type
=
60 dyn_cast
<clang::FunctionProtoType
>(&type_to_check
);
61 // Recurse to the return type and parameter types.
62 iter
->second
= IsAffected(*func_type
->getResultType().getTypePtr());
64 clang::FunctionProtoType::arg_type_iterator arg_iter
=
65 func_type
->arg_type_begin();
66 clang::FunctionProtoType::arg_type_iterator arg_end
=
67 func_type
->arg_type_end();
68 for (; (arg_iter
!= arg_end
) && (!iter
->second
); ++arg_iter
) {
69 iter
->second
= IsAffected(*(arg_iter
->getTypePtr()));
72 } else if (type_to_check
.isRecordType()) {
73 // For records (unions, structs), recurse to the fields.
74 const clang::RecordType
* record
=
75 dyn_cast
<clang::RecordType
>(&type_to_check
);
76 const clang::RecordDecl
* decl
= record
->getDecl();
77 clang::RecordDecl::field_iterator
field_iter(decl
->field_begin());
78 clang::RecordDecl::field_iterator
field_end(decl
->field_end());
79 for (; (field_iter
!= field_end
) && (!iter
->second
); ++field_iter
) {
80 iter
->second
= IsAffected(*(field_iter
->getType().getTypePtr()));
84 // By this point, the bool in the map at the location referenced by iter has
85 // the correct value. Either it was cached, or we computed & inserted it.
89 // Virtual function to consume top-level declarations. For each one, we check
90 // to see if it is a type definition. If it is, we print information about
92 virtual void HandleTopLevelDecl(clang::DeclGroupRef decl_group
) {
93 clang::DeclGroupRef::iterator
iter(decl_group
.begin());
94 clang::DeclGroupRef::iterator
the_end(decl_group
.end());
95 for (; iter
!= the_end
; ++iter
) {
96 const clang::Decl
*decl
= *iter
;
97 if (const clang::TypeDecl
* type_decl
= dyn_cast
<clang::TypeDecl
>(decl
)) {
98 std::string
name(type_decl
->getNameAsString());
99 // TagDecl covers structs, enums, unions, and classes.
100 if (const clang::TagDecl
* tag
= dyn_cast
<clang::TagDecl
>(type_decl
)) {
101 // Only print out info when we find the definition; ignore forward
103 if (tag
->isDefinition()) {
104 clang::Type
* type
= type_decl
->getTypeForDecl();
105 if (IsAffected(*type
)) {
106 std::printf("%s\n", name
.c_str());
109 } else if (const clang::TypedefDecl
* td
=
110 dyn_cast
<clang::TypedefDecl
>(type_decl
)) {
111 clang::Type
* type
= td
->getUnderlyingType().getTypePtr();
112 if (IsAffected(*type
)) {
113 std::printf("%s\n", name
.c_str());
121 class FindAffectedInterfacesAction
: public clang::PluginASTAction
{
123 FindAffectedInterfacesAction() {}
128 virtual clang::ASTConsumer
*CreateASTConsumer(
129 clang::CompilerInstance
&instance
, llvm::StringRef
/*input_file*/) {
130 return new FindAffectedInterfacesConsumer(types_
);
133 virtual bool ParseArgs(const clang::CompilerInstance
& /*instance*/,
134 const std::vector
<std::string
>& args
) {
135 // Every argument is interpreted as a comma-delimited list of names of types
136 // that have been changed.
137 StringVec::const_iterator
iter(args
.begin()), end(args
.end());
138 for (; iter
!= end
; ++iter
) {
139 std::stringstream
stream(*iter
);
140 std::string type_name
;
141 while (std::getline(stream
, type_name
, ',')) {
142 types_
.push_back(type_name
);
151 static clang::FrontendPluginRegistry::Add
<FindAffectedInterfacesAction
>
152 X("FindAffectedInterfaces",
153 "Find interfaces affected by changes to the passes classes.");