llvm-project/clang/unittests/StaticAnalyzer/NoStateChangeFuncVisitorTest.cpp
serge-sans-paille d9ab3e82f3
[clang] Use a StringRef instead of a raw char pointer to store builtin and call information
This avoids recomputing string length that is already known at compile time.

It has a slight impact on preprocessing / compile time, see

https://llvm-compile-time-tracker.com/compare.php?from=3f36d2d579d8b0e8824d9dd99bfa79f456858f88&to=e49640c507ddc6615b5e503144301c8e41f8f434&stat=instructions:u

This a recommit of e953ae5bbc313fd0cc980ce021d487e5b5199ea4 and the subsequent fixes caa713559bd38f337d7d35de35686775e8fb5175 and 06b90e2e9c991e211fecc97948e533320a825470.

The above patchset caused some version of GCC to take eons to compile clang/lib/Basic/Targets/AArch64.cpp, as spotted in aa171833ab0017d9732e82b8682c9848ab25ff9e.
The fix is to make BuiltinInfo tables a compilation unit static variable, instead of a private static variable.

Differential Revision: https://reviews.llvm.org/D139881
2022-12-27 09:55:19 +01:00

304 lines
9.0 KiB
C++

//===- unittests/StaticAnalyzer/NoStateChangeFuncVisitorTest.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 "CheckerRegistration.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "gtest/gtest.h"
#include <memory>
//===----------------------------------------------------------------------===//
// Base classes for testing NoStateChangeFuncVisitor.
//
// Testing is done by observing a very simple trait change from one node to
// another -- the checker sets the ErrorPrevented trait to true if
// 'preventError()' is called in the source code, and sets it to false if
// 'allowError()' is called. If this trait is false when 'error()' is called,
// a warning is emitted.
//
// The checker then registers a simple NoStateChangeFuncVisitor to add notes to
// inlined functions that could have, but neglected to prevent the error.
//===----------------------------------------------------------------------===//
REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrorPrevented, bool)
namespace clang {
namespace ento {
namespace {
class ErrorNotPreventedFuncVisitor : public NoStateChangeFuncVisitor {
public:
ErrorNotPreventedFuncVisitor()
: NoStateChangeFuncVisitor(bugreporter::TrackingKind::Thorough) {}
virtual PathDiagnosticPieceRef
maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
const ObjCMethodCall &Call,
const ExplodedNode *N) override {
return nullptr;
}
virtual PathDiagnosticPieceRef
maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
const CXXConstructorCall &Call,
const ExplodedNode *N) override {
return nullptr;
}
virtual PathDiagnosticPieceRef
maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call,
const ExplodedNode *N) override {
PathDiagnosticLocation L = PathDiagnosticLocation::create(
N->getLocation(),
N->getState()->getStateManager().getContext().getSourceManager());
return std::make_shared<PathDiagnosticEventPiece>(
L, "Returning without prevening the error");
}
void Profile(llvm::FoldingSetNodeID &ID) const override {
static int Tag = 0;
ID.AddPointer(&Tag);
}
};
template <class Visitor>
class StatefulChecker : public Checker<check::PreCall> {
mutable std::unique_ptr<BugType> BT;
public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const {
if (CallDescription{{"preventError"}, 0}.matches(Call)) {
C.addTransition(C.getState()->set<ErrorPrevented>(true));
return;
}
if (CallDescription{{"allowError"}, 0}.matches(Call)) {
C.addTransition(C.getState()->set<ErrorPrevented>(false));
return;
}
if (CallDescription{{"error"}, 0}.matches(Call)) {
if (C.getState()->get<ErrorPrevented>())
return;
const ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
if (!BT)
BT.reset(new BugType(this->getCheckerName(), "error()",
categories::SecurityError));
auto R =
std::make_unique<PathSensitiveBugReport>(*BT, "error() called", N);
R->template addVisitor<Visitor>();
C.emitReport(std::move(R));
}
}
};
} // namespace
} // namespace ento
} // namespace clang
//===----------------------------------------------------------------------===//
// Non-thorough analysis: only the state right before and right after the
// function call is checked for the difference in trait value.
//===----------------------------------------------------------------------===//
namespace clang {
namespace ento {
namespace {
class NonThoroughErrorNotPreventedFuncVisitor
: public ErrorNotPreventedFuncVisitor {
public:
virtual bool
wasModifiedInFunction(const ExplodedNode *CallEnterN,
const ExplodedNode *CallExitEndN) override {
return CallEnterN->getState()->get<ErrorPrevented>() !=
CallExitEndN->getState()->get<ErrorPrevented>();
}
};
void addNonThoroughStatefulChecker(AnalysisASTConsumer &AnalysisConsumer,
AnalyzerOptions &AnOpts) {
AnOpts.CheckersAndPackages = {{"test.StatefulChecker", true}};
AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
Registry
.addChecker<StatefulChecker<NonThoroughErrorNotPreventedFuncVisitor>>(
"test.StatefulChecker", "Description", "");
});
}
TEST(NoStateChangeFuncVisitor, NonThoroughFunctionAnalysis) {
std::string Diags;
EXPECT_TRUE(runCheckerOnCode<addNonThoroughStatefulChecker>(R"(
void error();
void preventError();
void allowError();
void g() {
//preventError();
}
void f() {
g();
error();
}
)", Diags));
EXPECT_EQ(Diags,
"test.StatefulChecker: Calling 'g' | Returning without prevening "
"the error | Returning from 'g' | error() called\n");
Diags.clear();
EXPECT_TRUE(runCheckerOnCode<addNonThoroughStatefulChecker>(R"(
void error();
void preventError();
void allowError();
void g() {
preventError();
allowError();
}
void f() {
g();
error();
}
)", Diags));
EXPECT_EQ(Diags,
"test.StatefulChecker: Calling 'g' | Returning without prevening "
"the error | Returning from 'g' | error() called\n");
Diags.clear();
EXPECT_TRUE(runCheckerOnCode<addNonThoroughStatefulChecker>(R"(
void error();
void preventError();
void allowError();
void g() {
preventError();
}
void f() {
g();
error();
}
)", Diags));
EXPECT_EQ(Diags, "");
}
} // namespace
} // namespace ento
} // namespace clang
//===----------------------------------------------------------------------===//
// Thorough analysis: only the state right before and right after the
// function call is checked for the difference in trait value.
//===----------------------------------------------------------------------===//
namespace clang {
namespace ento {
namespace {
class ThoroughErrorNotPreventedFuncVisitor
: public ErrorNotPreventedFuncVisitor {
public:
virtual bool
wasModifiedBeforeCallExit(const ExplodedNode *CurrN,
const ExplodedNode *CallExitBeginN) override {
return CurrN->getState()->get<ErrorPrevented>() !=
CallExitBeginN->getState()->get<ErrorPrevented>();
}
};
void addThoroughStatefulChecker(AnalysisASTConsumer &AnalysisConsumer,
AnalyzerOptions &AnOpts) {
AnOpts.CheckersAndPackages = {{"test.StatefulChecker", true}};
AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
Registry.addChecker<StatefulChecker<ThoroughErrorNotPreventedFuncVisitor>>(
"test.StatefulChecker", "Description", "");
});
}
TEST(NoStateChangeFuncVisitor, ThoroughFunctionAnalysis) {
std::string Diags;
EXPECT_TRUE(runCheckerOnCode<addThoroughStatefulChecker>(R"(
void error();
void preventError();
void allowError();
void g() {
//preventError();
}
void f() {
g();
error();
}
)", Diags));
EXPECT_EQ(Diags,
"test.StatefulChecker: Calling 'g' | Returning without prevening "
"the error | Returning from 'g' | error() called\n");
Diags.clear();
EXPECT_TRUE(runCheckerOnCode<addThoroughStatefulChecker>(R"(
void error();
void preventError();
void allowError();
void g() {
preventError();
allowError();
}
void f() {
g();
error();
}
)", Diags));
EXPECT_EQ(Diags, "test.StatefulChecker: error() called\n");
Diags.clear();
EXPECT_TRUE(runCheckerOnCode<addThoroughStatefulChecker>(R"(
void error();
void preventError();
void allowError();
void g() {
preventError();
}
void f() {
g();
error();
}
)", Diags));
EXPECT_EQ(Diags, "");
}
} // namespace
} // namespace ento
} // namespace clang