slang_rs_check_ast.cpp revision f5b882cc561f656eb650d0fa6db7bac57989ab12
1/*
2 * Copyright 2012, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "clang/AST/Attr.h"
18
19#include "slang_rs_check_ast.h"
20
21#include "slang_assert.h"
22#include "slang.h"
23#include "slang_rs_export_foreach.h"
24#include "slang_rs_export_type.h"
25
26namespace slang {
27
28void RSCheckAST::VisitStmt(clang::Stmt *S) {
29  // This function does the actual iteration through all sub-Stmt's within
30  // a given Stmt. Note that this function is skipped by all of the other
31  // Visit* functions if we have already found a higher-level match.
32  for (clang::Stmt::child_iterator I = S->child_begin(), E = S->child_end();
33       I != E;
34       I++) {
35    if (clang::Stmt *Child = *I) {
36      Visit(Child);
37    }
38  }
39}
40
41void RSCheckAST::WarnOnSetElementAt(clang::CallExpr *E) {
42  clang::FunctionDecl *Decl;
43  Decl = clang::dyn_cast_or_null<clang::FunctionDecl>(E->getCalleeDecl());
44
45  if (!Decl || Decl->getNameAsString() != std::string("rsSetElementAt")) {
46    return;
47  }
48
49  clang::Expr *Expr;
50  clang::ImplicitCastExpr *ImplCast;
51  Expr = E->getArg(1);
52  ImplCast = clang::dyn_cast_or_null<clang::ImplicitCastExpr>(Expr);
53
54  if (!ImplCast) {
55    return;
56  }
57
58  const clang::Type *Ty;
59  const clang::VectorType *VectorTy;
60  const clang::BuiltinType *ElementTy;
61  Ty = ImplCast->getSubExpr()->getType()->getPointeeType()
62    ->getUnqualifiedDesugaredType();
63  VectorTy = clang::dyn_cast_or_null<clang::VectorType>(Ty);
64
65  if (VectorTy) {
66    ElementTy = clang::dyn_cast_or_null<clang::BuiltinType>(
67      VectorTy->getElementType()->getUnqualifiedDesugaredType());
68  } else {
69    ElementTy = clang::dyn_cast_or_null<clang::BuiltinType>(
70      Ty->getUnqualifiedDesugaredType());
71  }
72
73  if (!ElementTy) {
74    return;
75  }
76
77  // We only support vectors with 2, 3 or 4 elements.
78  if (VectorTy) {
79    switch (VectorTy->getNumElements()) {
80    default:
81      return;
82    case 2:
83    case 3:
84    case 4:
85      break;
86    }
87  }
88
89  const char *Name;
90
91  switch (ElementTy->getKind()) {
92    case clang::BuiltinType::Float:
93      Name = "float";
94      break;
95    case clang::BuiltinType::Double:
96      Name = "double";
97      break;
98    case clang::BuiltinType::Char_S:
99      Name = "char";
100      break;
101    case clang::BuiltinType::Short:
102      Name = "short";
103      break;
104    case clang::BuiltinType::Int:
105      Name = "int";
106      break;
107    case clang::BuiltinType::Long:
108      Name = "long";
109      break;
110    case clang::BuiltinType::UChar:
111      Name = "uchar";
112      break;
113    case clang::BuiltinType::UShort:
114      Name = "ushort";
115      break;
116    case clang::BuiltinType::UInt:
117      Name = "uint";
118      break;
119    case clang::BuiltinType::ULong:
120      Name = "ulong";
121      break;
122    default:
123      return;
124  }
125
126  clang::DiagnosticBuilder DiagBuilder =
127      Context->ReportWarning(E->getLocStart(),
128                             "untyped rsSetElementAt() can reduce performance. "
129                             "Use rsSetElementAt_%0%1() instead.");
130  DiagBuilder << Name;
131
132  if (VectorTy) {
133    DiagBuilder << VectorTy->getNumElements();
134  } else {
135    DiagBuilder << "";
136  }
137}
138
139void RSCheckAST::VisitCallExpr(clang::CallExpr *E) {
140  WarnOnSetElementAt(E);
141
142  for (clang::CallExpr::arg_iterator AI = E->arg_begin(), AE = E->arg_end();
143       AI != AE; ++AI) {
144    Visit(*AI);
145  }
146}
147
148void RSCheckAST::ValidateFunctionDecl(clang::FunctionDecl *FD) {
149  if (!FD) {
150    return;
151  }
152
153  // Validate that the kernel attribute is not used with static.
154  if (FD->hasAttr<clang::KernelAttr>() &&
155      FD->getStorageClass() == clang::SC_Static) {
156    Context->ReportError(FD->getLocation(),
157                         "Invalid use of attribute kernel with "
158                         "static function declaration: %0")
159        << FD->getName();
160    mValid = false;
161  }
162
163  clang::QualType resultType = FD->getReturnType().getCanonicalType();
164  bool isExtern = (FD->getFormalLinkage() == clang::ExternalLinkage);
165
166  // We use FD as our NamedDecl in the case of a bad return type.
167  if (!RSExportType::ValidateType(Context, C, resultType, FD,
168                                  FD->getLocStart(), mTargetAPI,
169                                  mIsFilterscript, isExtern)) {
170    mValid = false;
171  }
172
173  size_t numParams = FD->getNumParams();
174  for (size_t i = 0; i < numParams; i++) {
175    clang::ParmVarDecl *PVD = FD->getParamDecl(i);
176    clang::QualType QT = PVD->getType().getCanonicalType();
177    if (!RSExportType::ValidateType(Context, C, QT, PVD, PVD->getLocStart(),
178                                    mTargetAPI, mIsFilterscript, isExtern)) {
179      mValid = false;
180    }
181  }
182
183  bool saveKernel = mInKernel;
184  mInKernel = RSExportForEach::isRSForEachFunc(mTargetAPI, FD);
185
186  if (clang::Stmt *Body = FD->getBody()) {
187    Visit(Body);
188  }
189
190  mInKernel = saveKernel;
191}
192
193
194void RSCheckAST::ValidateVarDecl(clang::VarDecl *VD) {
195  if (!VD) {
196    return;
197  }
198
199  clang::QualType QT = VD->getType();
200
201  if (VD->getFormalLinkage() == clang::ExternalLinkage) {
202    llvm::StringRef TypeName;
203    const clang::Type *T = QT.getTypePtr();
204    if (!RSExportType::NormalizeType(T, TypeName, Context, VD)) {
205      mValid = false;
206    }
207  }
208
209  // We don't allow static (non-const) variables within kernels.
210  if (mInKernel && VD->isStaticLocal()) {
211    if (!QT.isConstQualified()) {
212      Context->ReportError(
213          VD->getLocation(),
214          "Non-const static variables are not allowed in kernels: '%0'")
215          << VD->getName();
216      mValid = false;
217    }
218  }
219
220  if (!RSExportType::ValidateVarDecl(Context, VD, mTargetAPI, mIsFilterscript)) {
221    mValid = false;
222  } else if (clang::Expr *Init = VD->getInit()) {
223    // Only check the initializer if the decl is already ok.
224    Visit(Init);
225  }
226}
227
228
229void RSCheckAST::VisitDeclStmt(clang::DeclStmt *DS) {
230  if (!Slang::IsLocInRSHeaderFile(DS->getLocStart(), mSM)) {
231    for (clang::DeclStmt::decl_iterator I = DS->decl_begin(),
232                                        E = DS->decl_end();
233         I != E;
234         ++I) {
235      if (clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*I)) {
236        ValidateVarDecl(VD);
237      } else if (clang::FunctionDecl *FD =
238            llvm::dyn_cast<clang::FunctionDecl>(*I)) {
239        ValidateFunctionDecl(FD);
240      }
241    }
242  }
243}
244
245
246void RSCheckAST::VisitCastExpr(clang::CastExpr *CE) {
247  if (CE->getCastKind() == clang::CK_BitCast) {
248    clang::QualType QT = CE->getType();
249    const clang::Type *T = QT.getTypePtr();
250    if (T->isVectorType()) {
251      if (llvm::isa<clang::ImplicitCastExpr>(CE)) {
252        Context->ReportError(CE->getExprLoc(), "invalid implicit vector cast");
253      } else {
254        Context->ReportError(CE->getExprLoc(), "invalid vector cast");
255      }
256      mValid = false;
257    }
258  }
259  Visit(CE->getSubExpr());
260}
261
262
263void RSCheckAST::VisitExpr(clang::Expr *E) {
264  // This is where FS checks for code using pointer and/or 64-bit expressions
265  // (i.e. things like casts).
266
267  // First we skip implicit casts (things like function calls and explicit
268  // array accesses rely heavily on them and they are valid.
269  E = E->IgnoreImpCasts();
270
271  // Expressions at this point in the checker are not externally visible.
272  static const bool kIsExtern = false;
273
274  if (mIsFilterscript &&
275      !Slang::IsLocInRSHeaderFile(E->getExprLoc(), mSM) &&
276      !RSExportType::ValidateType(Context, C, E->getType(), nullptr, E->getExprLoc(),
277                                  mTargetAPI, mIsFilterscript, kIsExtern)) {
278    mValid = false;
279  } else {
280    // Only visit sub-expressions if we haven't already seen a violation.
281    VisitStmt(E);
282  }
283}
284
285
286bool RSCheckAST::Validate() {
287  clang::TranslationUnitDecl *TUDecl = C.getTranslationUnitDecl();
288  for (clang::DeclContext::decl_iterator DI = TUDecl->decls_begin(),
289          DE = TUDecl->decls_end();
290       DI != DE;
291       DI++) {
292    if (!Slang::IsLocInRSHeaderFile(DI->getLocStart(), mSM)) {
293      if (clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*DI)) {
294        ValidateVarDecl(VD);
295      } else if (clang::FunctionDecl *FD =
296            llvm::dyn_cast<clang::FunctionDecl>(*DI)) {
297        ValidateFunctionDecl(FD);
298      } else if (clang::Stmt *Body = (*DI)->getBody()) {
299        Visit(Body);
300      }
301    }
302  }
303
304  return mValid;
305}
306
307}  // namespace slang
308