slang_rs_check_ast.cpp revision 089cde338148fbb75825aea4539ccdae8211ffef
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 "slang_rs_check_ast.h"
18
19#include "slang_assert.h"
20#include "slang_rs.h"
21#include "slang_rs_export_foreach.h"
22#include "slang_rs_export_type.h"
23
24namespace slang {
25
26void RSCheckAST::VisitStmt(clang::Stmt *S) {
27  // This function does the actual iteration through all sub-Stmt's within
28  // a given Stmt. Note that this function is skipped by all of the other
29  // Visit* functions if we have already found a higher-level match.
30  for (clang::Stmt::child_iterator I = S->child_begin(), E = S->child_end();
31       I != E;
32       I++) {
33    if (clang::Stmt *Child = *I) {
34      Visit(Child);
35    }
36  }
37}
38
39void RSCheckAST::ValidateFunctionDecl(clang::FunctionDecl *FD) {
40  if (!FD) {
41    return;
42  }
43
44  if (mIsFilterscript) {
45    // Validate parameters for Filterscript.
46    size_t numParams = FD->getNumParams();
47
48    clang::QualType resultType = FD->getResultType().getCanonicalType();
49
50    // We use FD as our NamedDecl in the case of a bad return type.
51    if (!RSExportType::ValidateType(C, resultType, FD,
52                                    FD->getLocStart(), mTargetAPI,
53                                    mIsFilterscript)) {
54      mValid = false;
55    }
56
57    for (size_t i = 0; i < numParams; i++) {
58      clang::ParmVarDecl *PVD = FD->getParamDecl(i);
59      clang::QualType QT = PVD->getType().getCanonicalType();
60      if (!RSExportType::ValidateType(C, QT, PVD, PVD->getLocStart(),
61                                      mTargetAPI, mIsFilterscript)) {
62        mValid = false;
63      }
64    }
65  }
66
67  bool saveKernel = mInKernel;
68  mInKernel = RSExportForEach::isRSForEachFunc(mTargetAPI, &mDiagEngine, FD);
69
70  if (clang::Stmt *Body = FD->getBody()) {
71    Visit(Body);
72  }
73
74  mInKernel = saveKernel;
75}
76
77
78void RSCheckAST::ValidateVarDecl(clang::VarDecl *VD) {
79  if (!VD) {
80    return;
81  }
82
83  clang::QualType QT = VD->getType();
84
85  if (VD->getLinkage() == clang::ExternalLinkage) {
86    llvm::StringRef TypeName;
87    const clang::Type *T = QT.getTypePtr();
88    if (!RSExportType::NormalizeType(T, TypeName, &mDiagEngine, VD)) {
89      mValid = false;
90    }
91  }
92
93  // We don't allow static (non-const) variables within kernels.
94  if (mInKernel && VD->isStaticLocal()) {
95    if (!QT.isConstQualified()) {
96      mDiagEngine.Report(
97        clang::FullSourceLoc(VD->getLocation(), mSM),
98        mDiagEngine.getCustomDiagID(
99          clang::DiagnosticsEngine::Error,
100          "Non-const static variables are not allowed in kernels: '%0'"))
101          << VD->getName();
102      mValid = false;
103    }
104  }
105
106  if (!RSExportType::ValidateVarDecl(VD, mTargetAPI, mIsFilterscript)) {
107    mValid = false;
108  } else if (clang::Expr *Init = VD->getInit()) {
109    // Only check the initializer if the decl is already ok.
110    Visit(Init);
111  }
112}
113
114
115void RSCheckAST::VisitDeclStmt(clang::DeclStmt *DS) {
116  if (!SlangRS::IsLocInRSHeaderFile(DS->getLocStart(), mSM)) {
117    for (clang::DeclStmt::decl_iterator I = DS->decl_begin(),
118                                        E = DS->decl_end();
119         I != E;
120         ++I) {
121      if (clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*I)) {
122        ValidateVarDecl(VD);
123      } else if (clang::FunctionDecl *FD =
124            llvm::dyn_cast<clang::FunctionDecl>(*I)) {
125        ValidateFunctionDecl(FD);
126      }
127    }
128  }
129}
130
131
132void RSCheckAST::VisitExpr(clang::Expr *E) {
133  // This is where FS checks for code using pointer and/or 64-bit expressions
134  // (i.e. things like casts).
135
136  // First we skip implicit casts (things like function calls and explicit
137  // array accesses rely heavily on them and they are valid.
138  E = E->IgnoreImpCasts();
139  if (mIsFilterscript &&
140      !SlangRS::IsLocInRSHeaderFile(E->getExprLoc(), mSM) &&
141      !RSExportType::ValidateType(C, E->getType(), NULL, E->getExprLoc(),
142                                  mTargetAPI, mIsFilterscript)) {
143    mValid = false;
144  } else {
145    // Only visit sub-expressions if we haven't already seen a violation.
146    VisitStmt(E);
147  }
148}
149
150
151bool RSCheckAST::Validate() {
152  clang::TranslationUnitDecl *TUDecl = C.getTranslationUnitDecl();
153  for (clang::DeclContext::decl_iterator DI = TUDecl->decls_begin(),
154          DE = TUDecl->decls_end();
155       DI != DE;
156       DI++) {
157    if (!SlangRS::IsLocInRSHeaderFile(DI->getLocStart(), mSM)) {
158      if (clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*DI)) {
159        ValidateVarDecl(VD);
160      } else if (clang::FunctionDecl *FD =
161            llvm::dyn_cast<clang::FunctionDecl>(*DI)) {
162        ValidateFunctionDecl(FD);
163      } else if (clang::Stmt *Body = (*DI)->getBody()) {
164        Visit(Body);
165      }
166    }
167  }
168
169  return mValid;
170}
171
172}  // namespace slang
173