OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2016 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 #include "clang/Frontend/FrontendPluginRegistry.h" | |
6 #include "clang/AST/AST.h" | |
7 #include "clang/AST/ASTConsumer.h" | |
8 #include "clang/AST/RecursiveASTVisitor.h" | |
9 #include "clang/Frontend/CompilerInstance.h" | |
10 #include "llvm/Support/raw_ostream.h" | |
11 | |
12 #include <stack> | |
13 | |
14 using namespace clang; | |
15 | |
16 namespace { | |
17 | |
18 const char kWriteParamSizeT[] = | |
19 "[chromium-ipc] IPC::WriteParam() is called on size_t."; | |
20 | |
21 const char kWriteParamTemplate[] = | |
22 "[chromium-ipc] IPC::WriteParam() is called in a template instantiation " | |
23 "on a type equivalent to size_t."; | |
24 | |
25 const char kNoteInstantiation[] = | |
26 "instantiation: %0"; | |
27 | |
28 const char kTupleSizeT[] = | |
29 "[chromium-ipc] IPC::ParamTuple specialized with size_t."; | |
30 | |
31 | |
32 bool IsSizeT(ASTContext& context, QualType type) { | |
33 type = type.getLocalUnqualifiedType(); | |
34 if (!context.hasSameUnqualifiedType(type, context.getSizeType())) { | |
35 return false; | |
36 } | |
37 while (true) { | |
38 if (type.getAsString() == "size_t") { | |
39 return true; | |
40 } | |
41 QualType desugaredType = type.getSingleStepDesugaredType(context); | |
dcheng
2016/02/05 19:42:27
Nit: desugared_type
| |
42 if (desugaredType == type) { | |
43 return false; | |
44 } | |
45 type = desugaredType.getLocalUnqualifiedType(); | |
46 } | |
47 } | |
48 | |
49 class SizeTExprFinder: RecursiveASTVisitor<SizeTExprFinder> { | |
50 public: | |
51 static Expr* Find(ASTContext& context, Expr* expr) { | |
52 SizeTExprFinder finder(context); | |
53 finder.TraverseStmt(expr); | |
54 return finder.found_; | |
55 } | |
56 | |
57 private: | |
58 typedef RecursiveASTVisitor<SizeTExprFinder> Base; | |
59 friend Base; | |
60 | |
61 SizeTExprFinder(ASTContext& context): context_(context), found_(nullptr) {} | |
dcheng
2016/02/05 19:42:27
explicit
| |
62 | |
63 bool shouldVisitTemplateInstantiations() const { return true; } | |
64 | |
65 bool TraverseStmt(Stmt* stmt) { | |
66 if (found_) { | |
67 return false; | |
68 } | |
69 Expr* expr = dyn_cast_or_null<Expr>(stmt); | |
70 if (!expr) { | |
71 return false; | |
72 } | |
73 QualType type = expr->getType(); | |
74 if (!context_.hasSameUnqualifiedType(type, context_.getSizeType())) { | |
75 return false; | |
76 } | |
77 if (IsSizeT(context_, type)) { | |
78 found_ = expr; | |
79 return false; | |
80 } | |
81 return Base::TraverseStmt(expr); | |
82 } | |
83 | |
84 ASTContext& context_; | |
85 Expr* found_; | |
86 }; | |
87 | |
88 class Visitor: public RecursiveASTVisitor<Visitor> { | |
89 typedef RecursiveASTVisitor<Visitor> Base; | |
90 public: | |
91 Visitor(CompilerInstance& compiler, ASTContext& context) | |
92 : compiler_(compiler), context_(context) { | |
93 auto& diagnostics = compiler_.getDiagnostics(); | |
94 error_write_param_size_t_ = diagnostics.getCustomDiagID( | |
95 DiagnosticsEngine::Error, kWriteParamSizeT); | |
96 error_write_param_template_ = diagnostics.getCustomDiagID( | |
97 DiagnosticsEngine::Error, kWriteParamTemplate); | |
98 note_instantiation_ = diagnostics.getCustomDiagID( | |
99 DiagnosticsEngine::Note, kNoteInstantiation); | |
100 error_tuple_size_t_ = diagnostics.getCustomDiagID( | |
101 DiagnosticsEngine::Error, kTupleSizeT); | |
102 } | |
103 | |
104 bool shouldVisitTemplateInstantiations() const { return true; } | |
105 | |
106 bool VisitTemplateSpecializationType(TemplateSpecializationType* spec) { | |
107 TemplateDecl* decl = spec->getTemplateName().getAsTemplateDecl(); | |
108 if (decl && decl->getQualifiedNameAsString() == "IPC::ParamTuple") { | |
109 for (unsigned i = 0; i != spec->getNumArgs(); ++i) { | |
110 QualType arg_type = spec->getArg(i).getAsType(); | |
111 if (IsSizeT(context_, arg_type)) { | |
112 compiler_.getDiagnostics().Report( | |
113 getParentDecl()->getLocStart(), error_tuple_size_t_); | |
114 break; | |
115 } | |
116 } | |
117 } | |
118 return true; | |
119 } | |
120 | |
121 bool VisitCallExpr(CallExpr* call) { | |
122 const FunctionDecl* callee = call->getDirectCallee(); | |
123 if (!callee || callee->getQualifiedNameAsString() != "IPC::WriteParam") { | |
124 return true; | |
125 } | |
126 | |
127 // TODO: what about WriteParam<size_t>((__SIZE_TYPE__)0)? | |
128 // It will be caught by missing unsigned long ParamTraits | |
129 // specialization, but we can also catch it here. | |
130 | |
131 // Don't bother if we're not called with __SIZE_TYPE__ | |
132 bool called_with_size_type = false; | |
133 for (const Expr* argument: call->arguments()) { | |
134 if (context_.hasSameUnqualifiedType( | |
135 argument->getType(), context_.getSizeType())) { | |
136 called_with_size_type = true; | |
137 break; | |
138 } | |
139 } | |
140 if (!called_with_size_type) { | |
141 return true; | |
142 } | |
143 | |
144 // TODO: what about lambdas? Do we need to search more? | |
145 if (auto parent = dyn_cast_or_null<FunctionDecl>(getParentDecl())) { | |
146 if (CheckWriteParamInstantiation(call, parent)) { | |
147 return true; | |
148 } | |
149 } | |
150 | |
151 for (Expr* argument: call->arguments()) { | |
152 Expr* sizet_expr = SizeTExprFinder::Find(context_, argument); | |
153 if (sizet_expr) { | |
154 compiler_.getDiagnostics().Report( | |
155 sizet_expr->getExprLoc(), error_write_param_size_t_); | |
156 } | |
157 } | |
158 | |
159 return true; | |
160 } | |
161 | |
162 bool TraverseDecl(Decl* decl) { | |
163 parent_decls_.push(decl); | |
164 bool result = Base::TraverseDecl(decl); | |
165 parent_decls_.pop(); | |
166 return result; | |
167 } | |
168 | |
169 private: | |
170 Decl* getParentDecl() const { | |
dcheng
2016/02/05 19:42:27
GetParentDecl()?
| |
171 return parent_decls_.empty() ? nullptr : parent_decls_.top(); | |
172 } | |
173 | |
174 bool CheckWriteParamInstantiation(CallExpr* call, FunctionDecl* parent) { | |
175 // Only check implicit instantiations | |
176 if (parent->getTemplatedKind() == FunctionDecl::TK_NonTemplate || | |
177 parent->getTemplateSpecializationKind() != TSK_ImplicitInstantiation) { | |
178 return false; | |
179 } | |
180 | |
181 // Check that we are inside IPC::ParamTraits<IPC::ParamTuple<..>> | |
182 bool param_tuple_specialization = false; | |
183 if (auto cxx_method = dyn_cast<CXXMethodDecl>(parent)) { | |
184 auto spec = dyn_cast<ClassTemplateSpecializationDecl>( | |
185 cxx_method->getParent()); | |
186 if (spec && spec->getQualifiedNameAsString() == "IPC::ParamTraits") { | |
187 const TemplateArgument& arg = spec->getTemplateArgs().get(0); | |
188 auto arg_type = dyn_cast<TagType>(arg.getAsType().getTypePtr()); | |
189 param_tuple_specialization = | |
190 arg_type && | |
191 arg_type->getDecl()->getQualifiedNameAsString() == | |
192 "IPC::ParamTuple"; | |
193 } | |
194 } | |
195 if (param_tuple_specialization) { | |
196 return false; | |
197 } | |
198 | |
199 compiler_.getDiagnostics().Report( | |
200 call->getExprLoc(), error_write_param_template_); | |
201 | |
202 // TODO: use TemplateSpecializationType::PrintTemplateArgumentList to | |
203 // include function's template arguments | |
204 compiler_.getDiagnostics().Report(call->getExprLoc(), note_instantiation_) | |
205 << parent->getQualifiedNameAsString(); | |
206 return true; | |
207 } | |
208 | |
209 CompilerInstance& compiler_; | |
210 ASTContext& context_; | |
211 | |
212 unsigned error_write_param_size_t_; | |
213 unsigned error_write_param_template_; | |
214 unsigned error_tuple_size_t_; | |
215 unsigned note_instantiation_; | |
216 | |
217 std::stack<Decl*> parent_decls_; | |
218 }; | |
219 | |
220 class Consumer: public ASTConsumer { | |
221 public: | |
222 Consumer(CompilerInstance& compiler): compiler_(compiler) {} | |
223 | |
224 void HandleTranslationUnit(ASTContext& context) override { | |
225 Visitor(compiler_, context).TraverseDecl(context.getTranslationUnitDecl()); | |
226 } | |
227 | |
228 private: | |
229 CompilerInstance& compiler_; | |
230 }; | |
231 | |
232 class Plugin: public PluginASTAction { | |
233 protected: | |
234 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance& compiler, | |
235 llvm::StringRef) override { | |
236 return llvm::make_unique<Consumer>(compiler); | |
237 } | |
238 | |
239 bool ParseArgs(const CompilerInstance&, | |
240 const std::vector<std::string>&) override { | |
241 return true; | |
242 } | |
243 }; | |
244 | |
245 } // namespace | |
246 | |
247 static FrontendPluginRegistry::Add<Plugin> X( | |
248 "check-ipc", | |
249 "Checks for size_t in IPC messages"); | |
OLD | NEW |