================ @@ -0,0 +1,284 @@ +//===- UncheckedStatusOrAccessModel.cpp -----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h" + +#include <cassert> +#include <utility> + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/TypeBase.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchersInternal.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h" +#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h" +#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" +#include "clang/Analysis/FlowSensitive/MatchSwitch.h" +#include "clang/Analysis/FlowSensitive/StorageLocation.h" +#include "clang/Analysis/FlowSensitive/Value.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/StringMap.h" + +namespace clang::dataflow::statusor_model { +namespace { + +using ::clang::ast_matchers::MatchFinder; +using ::clang::ast_matchers::StatementMatcher; + +} // namespace + +static bool isStatusOrOperatorBaseType(QualType type) { + return isRecordTypeWithName(type, "absl::internal_statusor::OperatorBase"); +} + +static bool isSafeUnwrap(RecordStorageLocation *StatusOrLoc, + const Environment &Env) { + if (!StatusOrLoc) + return false; + auto &StatusLoc = locForStatus(*StatusOrLoc); + auto *OkVal = Env.get<BoolValue>(locForOk(StatusLoc)); + return OkVal != nullptr && Env.proves(OkVal->formula()); +} + +static ClassTemplateSpecializationDecl * +getStatusOrBaseClass(const QualType &Ty) { + auto *RD = Ty->getAsCXXRecordDecl(); + if (RD == nullptr) + return nullptr; + if (isStatusOrType(Ty) || + // In case we are analyzing code under OperatorBase itself that uses + // operator* (e.g. to implement operator->). + isStatusOrOperatorBaseType(Ty)) + return cast<ClassTemplateSpecializationDecl>(RD); + if (!RD->hasDefinition()) + return nullptr; + for (const auto &Base : RD->bases()) + if (auto *QT = getStatusOrBaseClass(Base.getType())) + return QT; + return nullptr; +} + +static QualType getStatusOrValueType(ClassTemplateSpecializationDecl *TRD) { + return TRD->getTemplateArgs().get(0).getAsType(); +} + +static auto isStatusOrMemberCallWithName(llvm::StringRef member_name) { + using namespace ::clang::ast_matchers; // NOLINT: Too many names + return cxxMemberCallExpr( + on(expr(unless(cxxThisExpr()))), + callee(cxxMethodDecl( + hasName(member_name), + ofClass(anyOf(statusOrClass(), statusOrOperatorBaseClass()))))); +} + +static auto isStatusOrOperatorCallWithName(llvm::StringRef operator_name) { + using namespace ::clang::ast_matchers; // NOLINT: Too many names + return cxxOperatorCallExpr( + hasOverloadedOperatorName(operator_name), + callee(cxxMethodDecl( + ofClass(anyOf(statusOrClass(), statusOrOperatorBaseClass()))))); +} + +static auto valueCall() { + using namespace ::clang::ast_matchers; // NOLINT: Too many names + return anyOf(isStatusOrMemberCallWithName("value"), + isStatusOrMemberCallWithName("ValueOrDie")); +} + +static auto valueOperatorCall() { + using namespace ::clang::ast_matchers; // NOLINT: Too many names + return expr(anyOf(isStatusOrOperatorCallWithName("*"), + isStatusOrOperatorCallWithName("->"))); +} + +static auto +buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) { + return CFGMatchSwitchBuilder<const Environment, + llvm::SmallVector<SourceLocation>>() + // StatusOr::value, StatusOr::ValueOrDie + .CaseOfCFGStmt<CXXMemberCallExpr>( + valueCall(), + [](const CXXMemberCallExpr *E, + const ast_matchers::MatchFinder::MatchResult &, + const Environment &Env) { + if (!isSafeUnwrap(getImplicitObjectLocation(*E, Env), Env)) + return llvm::SmallVector<SourceLocation>({E->getExprLoc()}); + return llvm::SmallVector<SourceLocation>(); + }) + + // StatusOr::operator*, StatusOr::operator-> + .CaseOfCFGStmt<CXXOperatorCallExpr>( + valueOperatorCall(), + [](const CXXOperatorCallExpr *E, + const ast_matchers::MatchFinder::MatchResult &, + const Environment &Env) { + RecordStorageLocation *StatusOrLoc = + Env.get<RecordStorageLocation>(*E->getArg(0)); + if (!isSafeUnwrap(StatusOrLoc, Env)) + return llvm::SmallVector<SourceLocation>({E->getOperatorLoc()}); + return llvm::SmallVector<SourceLocation>(); + }) + .Build(); +} + +UncheckedStatusOrAccessDiagnoser::UncheckedStatusOrAccessDiagnoser( + UncheckedStatusOrAccessModelOptions Options) + : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} + +llvm::SmallVector<SourceLocation> UncheckedStatusOrAccessDiagnoser::operator()( + const CFGElement &Elt, ASTContext &Ctx, + const TransferStateForDiagnostics<UncheckedStatusOrAccessModel::Lattice> + &State) { + return DiagnoseMatchSwitch(Elt, Ctx, State.Env); +} + +BoolValue &initializeStatus(RecordStorageLocation &StatusLoc, + Environment &Env) { + auto &OkVal = Env.makeAtomicBoolValue(); + Env.setValue(locForOk(StatusLoc), OkVal); + return OkVal; +} + +BoolValue &initializeStatusOr(RecordStorageLocation &StatusOrLoc, + Environment &Env) { + return initializeStatus(locForStatus(StatusOrLoc), Env); +} + +clang::ast_matchers::DeclarationMatcher statusOrClass() { + using namespace ::clang::ast_matchers; // NOLINT: Too many names + return classTemplateSpecializationDecl( + hasName("absl::StatusOr"), + hasTemplateArgument(0, refersToType(type().bind("T")))); +} + +clang::ast_matchers::DeclarationMatcher statusClass() { + using namespace ::clang::ast_matchers; // NOLINT: Too many names + return cxxRecordDecl(hasName("absl::Status")); +} + +clang::ast_matchers::DeclarationMatcher statusOrOperatorBaseClass() { + using namespace ::clang::ast_matchers; // NOLINT: Too many names + return classTemplateSpecializationDecl( + hasName("absl::internal_statusor::OperatorBase")); +} + +clang::ast_matchers::TypeMatcher possiblyAliasedStatusOrType() { + using namespace ::clang::ast_matchers; // NOLINT: Too many names + return hasUnqualifiedDesugaredType( + recordType(hasDeclaration(statusOrClass()))); +} + +clang::ast_matchers::TypeMatcher possiblyAliasedStatusType() { + using namespace ::clang::ast_matchers; // NOLINT: Too many names + return hasUnqualifiedDesugaredType(recordType(hasDeclaration(statusClass()))); +} + +clang::ast_matchers::TypeMatcher statusOrType() { + using namespace ::clang::ast_matchers; // NOLINT: Too many names + return hasCanonicalType(qualType(hasDeclaration(statusOrClass()))); +} + +bool isRecordTypeWithName(QualType Type, llvm::StringRef TypeName) { + return Type->isRecordType() && + Type->getAsCXXRecordDecl()->getQualifiedNameAsString() == TypeName; ---------------- fmayer wrote:
done. added helper. will work in getting it in a good place in a follow up. https://github.com/llvm/llvm-project/pull/162932 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
