llvm/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

//===- unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.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 "TestingSupport.h"
#include "clang/AST/Decl.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/OperationKinds.h"
#include "clang/AST/Type.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
#include "clang/Analysis/FlowSensitive/DebugSupport.h"
#include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
#include "clang/Analysis/FlowSensitive/Value.h"
#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Testing/ADT/StringMapEntry.h"
#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <cassert>
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <utility>
#include <vector>

namespace {

usingnamespaceclang;
usingnamespacedataflow;
usingnamespacetest;
usingnamespaceast_matchers;
IsStringMapEntry;
DescribeMatcher;
IsEmpty;
NotNull;
Test;
UnorderedElementsAre;

class DataflowAnalysisTest : public Test {};

TEST_F(DataflowAnalysisTest, NoopAnalysis) {}

// Basic test that `diagnoseFunction` calls the Diagnoser function for the
// number of elements expected.
TEST_F(DataflowAnalysisTest, DiagnoseFunctionDiagnoserCalledOnEachElement) {}

TEST_F(DataflowAnalysisTest, CanAnalyzeStmt) {}

// Tests for the statement-to-block map.
StmtToBlockTest;

TEST_F(StmtToBlockTest, ConditionalOperator) {}

TEST_F(StmtToBlockTest, LogicalAnd) {}

TEST_F(StmtToBlockTest, IfStatementWithLogicalAnd) {}

// Tests that check we discard state for expressions correctly.
DiscardExprStateTest;

TEST_F(DiscardExprStateTest, WhileStatement) {}

TEST_F(DiscardExprStateTest, BooleanOperator) {}

TEST_F(DiscardExprStateTest, ConditionalOperator) {}

TEST_F(DiscardExprStateTest, CallWithParenExprTreatedCorrectly) {}

struct NonConvergingLattice {};

class NonConvergingAnalysis
    : public DataflowAnalysis<NonConvergingAnalysis, NonConvergingLattice> {};

TEST_F(DataflowAnalysisTest, NonConvergingAnalysis) {}

// Regression test for joins of bool-typed lvalue expressions. The first loop
// results in two passes through the code that follows. Each pass results in a
// different `StorageLocation` for the pointee of `v`. Then, the second loop
// causes a join at the loop head where the two environments map expresssion
// `*v` to different `StorageLocation`s.
//
// An earlier version crashed for this condition (for boolean-typed lvalues), so
// this test only verifies that the analysis runs successfully, without
// examining any details of the results.
TEST_F(DataflowAnalysisTest, JoinBoolLValues) {}

struct FunctionCallLattice {};

std::ostream &operator<<(std::ostream &OS, const FunctionCallLattice &L) {}

class FunctionCallAnalysis
    : public DataflowAnalysis<FunctionCallAnalysis, FunctionCallLattice> {};

class NoreturnDestructorTest : public Test {};

MATCHER_P(HoldsFunctionCallLattice, m,
          ((negation ? "doesn't hold" : "holds") +
           llvm::StringRef(" a lattice element that ") +
           DescribeMatcher<FunctionCallLattice>(m))
              .str()) {}

MATCHER_P(HasCalledFunctions, m,
          ((negation ? "doesn't hold" : "holds") +
           llvm::StringRef(" a set of called functions that ") +
           DescribeMatcher<FunctionCallLattice::FunctionSet>(m))
              .str()) {}

TEST_F(NoreturnDestructorTest, ConditionalOperatorBothBranchesReturn) {}

TEST_F(NoreturnDestructorTest, ConditionalOperatorLeftBranchReturns) {}

TEST_F(NoreturnDestructorTest,
       ConditionalOperatorConstantCondition_LeftBranchReturns) {}

TEST_F(NoreturnDestructorTest, ConditionalOperatorRightBranchReturns) {}

TEST_F(NoreturnDestructorTest,
       ConditionalOperatorConstantCondition_RightBranchReturns) {}

TEST_F(NoreturnDestructorTest, ConditionalOperatorNestedBranchesDoNotReturn) {}

TEST_F(NoreturnDestructorTest, ConditionalOperatorNestedBranchReturns) {}

// Models an analysis that uses flow conditions.
class SpecialBoolAnalysis final
    : public DataflowAnalysis<SpecialBoolAnalysis, NoopLattice> {};

class JoinFlowConditionsTest : public Test {};

TEST_F(JoinFlowConditionsTest, JoinDistinctButProvablyEquivalentValues) {}

class NullPointerAnalysis final
    : public DataflowAnalysis<NullPointerAnalysis, NoopLattice> {};

class WideningTest : public Test {};

TEST_F(WideningTest, JoinDistinctValuesWithDistinctProperties) {}

TEST_F(WideningTest, JoinDistinctValuesWithSameProperties) {}

TEST_F(WideningTest, DistinctPointersToTheSameLocationAreEquivalent) {}

TEST_F(WideningTest, DistinctValuesWithSamePropertiesAreEquivalent) {}

TEST_F(WideningTest, DistinctValuesWithDifferentPropertiesWidenedToTop) {}

class FlowConditionTest : public Test {};

TEST_F(FlowConditionTest, IfStmtSingleVar) {}

TEST_F(FlowConditionTest, IfStmtSingleNegatedVar) {}

TEST_F(FlowConditionTest, WhileStmt) {}

TEST_F(FlowConditionTest, WhileStmtWithAssignmentInCondition) {}

TEST_F(FlowConditionTest, Conjunction) {}

TEST_F(FlowConditionTest, Disjunction) {}

TEST_F(FlowConditionTest, NegatedConjunction) {}

TEST_F(FlowConditionTest, DeMorgan) {}

TEST_F(FlowConditionTest, Join) {}

// Verifies that flow conditions are properly constructed even when the
// condition is not meaningfully interpreted.
//
// Note: currently, arbitrary function calls are uninterpreted, so the test
// exercises this case. If and when we change that, this test will not add to
// coverage (although it may still test a valuable case).
TEST_F(FlowConditionTest, OpaqueFlowConditionJoinsToOpaqueBool) {}

// Verifies that flow conditions are properly constructed even when the
// condition is not meaningfully interpreted.
//
// Note: currently, fields with recursive type calls are uninterpreted (beneath
// the first instance), so the test exercises this case. If and when we change
// that, this test will not add to coverage (although it may still test a
// valuable case).
TEST_F(FlowConditionTest, OpaqueFieldFlowConditionJoinsToOpaqueBool) {}

// Verifies that flow conditions are properly constructed even when the
// condition is not meaningfully interpreted. Adds to above by nesting the
// interestnig case inside a normal branch. This protects against degenerate
// solutions which only test for empty flow conditions, for example.
TEST_F(FlowConditionTest, OpaqueFlowConditionInsideBranchJoinsToOpaqueBool) {}

TEST_F(FlowConditionTest, PointerToBoolImplicitCast) {}

class TopAnalysis final : public DataflowAnalysis<TopAnalysis, NoopLattice> {};

class TopTest : public Test {};

// Tests that when Top is unused it remains Top.
TEST_F(TopTest, UnusedTopInitializer) {}

// Tests that when Top is unused it remains Top. Like above, but uses the
// assignment form rather than initialization, which uses Top as an lvalue that
// is *not* in an rvalue position.
TEST_F(TopTest, UnusedTopAssignment) {}

TEST_F(TopTest, UnusedTopJoinsToTop) {}

TEST_F(TopTest, TopUsedBeforeBranchJoinsToSameAtomicBool) {}

TEST_F(TopTest, TopUsedInBothBranchesJoinsToAtomic) {}

TEST_F(TopTest, TopUsedInBothBranchesWithoutPrecisionLoss) {}

TEST_F(TopTest, TopUnusedBeforeLoopHeadJoinsToTop) {}

TEST_F(TopTest, ForRangeStmtConverges) {}
} // namespace