/*
* Copyright 2012, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "slang_rs_check_ast.h"
#include "slang_assert.h"
#include "slang_rs.h"
#include "slang_rs_export_foreach.h"
#include "slang_rs_export_type.h"
namespace slang {
void RSCheckAST::VisitStmt(clang::Stmt *S) {
// This function does the actual iteration through all sub-Stmt's within
// a given Stmt. Note that this function is skipped by all of the other
// Visit* functions if we have already found a higher-level match.
for (clang::Stmt::child_iterator I = S->child_begin(), E = S->child_end();
I != E;
I++) {
if (clang::Stmt *Child = *I) {
Visit(Child);
}
}
}
void RSCheckAST::ValidateFunctionDecl(clang::FunctionDecl *FD) {
if (!FD) {
return;
}
if (mIsFilterscript) {
// Validate parameters for Filterscript.
size_t numParams = FD->getNumParams();
clang::QualType resultType = FD->getResultType().getCanonicalType();
// We use FD as our NamedDecl in the case of a bad return type.
if (!RSExportType::ValidateType(C, resultType, FD,
FD->getLocStart(), mTargetAPI,
mIsFilterscript)) {
mValid = false;
}
for (size_t i = 0; i < numParams; i++) {
clang::ParmVarDecl *PVD = FD->getParamDecl(i);
clang::QualType QT = PVD->getType().getCanonicalType();
if (!RSExportType::ValidateType(C, QT, PVD, PVD->getLocStart(),
mTargetAPI, mIsFilterscript)) {
mValid = false;
}
}
}
bool saveKernel = mInKernel;
mInKernel = RSExportForEach::isRSForEachFunc(mTargetAPI, &mDiagEngine, FD);
if (clang::Stmt *Body = FD->getBody()) {
Visit(Body);
}
mInKernel = saveKernel;
}
void RSCheckAST::ValidateVarDecl(clang::VarDecl *VD) {
if (!VD) {
return;
}
clang::QualType QT = VD->getType();
if (VD->getLinkage() == clang::ExternalLinkage) {
llvm::StringRef TypeName;
const clang::Type *T = QT.getTypePtr();
if (!RSExportType::NormalizeType(T, TypeName, &mDiagEngine, VD)) {
mValid = false;
}
}
// We don't allow static (non-const) variables within kernels.
if (mInKernel && VD->isStaticLocal()) {
if (!QT.isConstQualified()) {
mDiagEngine.Report(
clang::FullSourceLoc(VD->getLocation(), mSM),
mDiagEngine.getCustomDiagID(
clang::DiagnosticsEngine::Error,
"Non-const static variables are not allowed in kernels: '%0'"))
<< VD->getName();
mValid = false;
}
}
if (!RSExportType::ValidateVarDecl(VD, mTargetAPI, mIsFilterscript)) {
mValid = false;
} else if (clang::Expr *Init = VD->getInit()) {
// Only check the initializer if the decl is already ok.
Visit(Init);
}
}
void RSCheckAST::VisitDeclStmt(clang::DeclStmt *DS) {
if (!SlangRS::IsLocInRSHeaderFile(DS->getLocStart(), mSM)) {
for (clang::DeclStmt::decl_iterator I = DS->decl_begin(),
E = DS->decl_end();
I != E;
++I) {
if (clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*I)) {
ValidateVarDecl(VD);
} else if (clang::FunctionDecl *FD =
llvm::dyn_cast<clang::FunctionDecl>(*I)) {
ValidateFunctionDecl(FD);
}
}
}
}
void RSCheckAST::VisitExpr(clang::Expr *E) {
// This is where FS checks for code using pointer and/or 64-bit expressions
// (i.e. things like casts).
// First we skip implicit casts (things like function calls and explicit
// array accesses rely heavily on them and they are valid.
E = E->IgnoreImpCasts();
if (mIsFilterscript &&
!SlangRS::IsLocInRSHeaderFile(E->getExprLoc(), mSM) &&
!RSExportType::ValidateType(C, E->getType(), NULL, E->getExprLoc(),
mTargetAPI, mIsFilterscript)) {
mValid = false;
} else {
// Only visit sub-expressions if we haven't already seen a violation.
VisitStmt(E);
}
}
bool RSCheckAST::Validate() {
clang::TranslationUnitDecl *TUDecl = C.getTranslationUnitDecl();
for (clang::DeclContext::decl_iterator DI = TUDecl->decls_begin(),
DE = TUDecl->decls_end();
DI != DE;
DI++) {
if (!SlangRS::IsLocInRSHeaderFile(DI->getLocStart(), mSM)) {
if (clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*DI)) {
ValidateVarDecl(VD);
} else if (clang::FunctionDecl *FD =
llvm::dyn_cast<clang::FunctionDecl>(*DI)) {
ValidateFunctionDecl(FD);
} else if (clang::Stmt *Body = (*DI)->getBody()) {
Visit(Body);
}
}
}
return mValid;
}
} // namespace slang