aboutsummaryrefslogtreecommitdiff
path: root/include/clang/StaticAnalyzer
diff options
context:
space:
mode:
Diffstat (limited to 'include/clang/StaticAnalyzer')
-rw-r--r--include/clang/StaticAnalyzer/Checkers/Checkers.td43
-rw-r--r--include/clang/StaticAnalyzer/Core/AnalyzerOptions.def32
-rw-r--r--include/clang/StaticAnalyzer/Core/AnalyzerOptions.h68
-rw-r--r--include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h656
-rw-r--r--include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h217
-rw-r--r--include/clang/StaticAnalyzer/Core/BugReporter/BugType.h40
-rw-r--r--include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h1
-rw-r--r--include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h923
-rw-r--r--include/clang/StaticAnalyzer/Core/Checker.h4
-rw-r--r--include/clang/StaticAnalyzer/Core/CheckerManager.h24
-rw-r--r--include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h12
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h9
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h27
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h17
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h55
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h73
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h46
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h63
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h54
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h10
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h6
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h4
22 files changed, 924 insertions, 1460 deletions
diff --git a/include/clang/StaticAnalyzer/Checkers/Checkers.td b/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 2b29efba66a4..4d52655045b3 100644
--- a/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -504,6 +504,15 @@ def MoveChecker: Checker<"Move">,
]>,
Documentation<HasDocumentation>;
+def VirtualCallModeling : Checker<"VirtualCallModeling">,
+ HelpText<"Auxiliary modeling for the virtual method call checkers">,
+ Documentation<NotDocumented>,
+ Hidden;
+
+def PureVirtualCallChecker : Checker<"PureVirtualCall">,
+ HelpText<"Check pure virtual function calls during construction/destruction">,
+ Dependencies<[VirtualCallModeling]>,
+ Documentation<HasDocumentation>;
} // end: "cplusplus"
let ParentPackage = CplusplusOptIn in {
@@ -552,14 +561,22 @@ def UninitializedObjectChecker: Checker<"UninitializedObject">,
Documentation<HasAlphaDocumentation>;
def VirtualCallChecker : Checker<"VirtualCall">,
- HelpText<"Check virtual function calls during construction or destruction">,
+ HelpText<"Check virtual function calls during construction/destruction">,
CheckerOptions<[
CmdLineOption<Boolean,
+ "ShowFixIts",
+ "Enable fix-it hints for this checker",
+ "false",
+ InAlpha>,
+ CmdLineOption<Boolean,
"PureOnly",
- "Whether to only report calls to pure virtual methods.",
+ "Disables the checker. Keeps cplusplus.PureVirtualCall "
+ "enabled. This option is only provided for backwards "
+ "compatibility.",
"false",
- Released>
+ InAlpha>
]>,
+ Dependencies<[VirtualCallModeling]>,
Documentation<HasDocumentation>;
} // end: "optin.cplusplus"
@@ -636,6 +653,19 @@ let ParentPackage = DeadCode in {
def DeadStoresChecker : Checker<"DeadStores">,
HelpText<"Check for values stored to variables that are never read "
"afterwards">,
+ CheckerOptions<[
+ CmdLineOption<Boolean,
+ "WarnForDeadNestedAssignments",
+ "Warns for deadstores in nested assignments."
+ "E.g.: if ((P = f())) where P is unused.",
+ "true",
+ Released>,
+ CmdLineOption<Boolean,
+ "ShowFixIts",
+ "Enable fix-it hints for this checker",
+ "false",
+ InAlpha>
+ ]>,
Documentation<HasDocumentation>;
} // end DeadCode
@@ -799,6 +829,13 @@ let ParentPackage = Taint in {
def GenericTaintChecker : Checker<"TaintPropagation">,
HelpText<"Generate taint information used by other checkers">,
+ CheckerOptions<[
+ CmdLineOption<String,
+ "Config",
+ "Specifies the name of the configuration file.",
+ "",
+ InAlpha>,
+ ]>,
Documentation<HasAlphaDocumentation>;
} // end "alpha.security.taint"
diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
index 70bd476b6c43..d853fb74f9c7 100644
--- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
+++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
@@ -294,12 +294,20 @@ ANALYZER_OPTION(bool, DisplayCTUProgress, "display-ctu-progress",
ANALYZER_OPTION(bool, ShouldTrackConditions, "track-conditions",
"Whether to track conditions that are a control dependency of "
"an already tracked variable.",
- false)
+ true)
ANALYZER_OPTION(bool, ShouldTrackConditionsDebug, "track-conditions-debug",
"Whether to place an event at each tracked condition.",
false)
+ANALYZER_OPTION(bool, ShouldEmitFixItHintsAsRemarks, "fixits-as-remarks",
+ "Emit fix-it hints as remarks for testing purposes",
+ false)
+
+//===----------------------------------------------------------------------===//
+// Unsigned analyzer options.
+//===----------------------------------------------------------------------===//
+
ANALYZER_OPTION(unsigned, CTUImportThreshold, "ctu-import-threshold",
"The maximal amount of translation units that is considered "
"for import when inlining functions during CTU analysis. "
@@ -308,10 +316,6 @@ ANALYZER_OPTION(unsigned, CTUImportThreshold, "ctu-import-threshold",
"various translation units.",
100u)
-//===----------------------------------------------------------------------===//
-// Unsinged analyzer options.
-//===----------------------------------------------------------------------===//
-
ANALYZER_OPTION(
unsigned, AlwaysInlineSize, "ipa-always-inline-size",
"The size of the functions (in basic blocks), which should be considered "
@@ -380,12 +384,6 @@ ANALYZER_OPTION(
"Value: \"constructors\", \"destructors\", \"methods\".",
"destructors")
-ANALYZER_OPTION_DEPENDS_ON_USER_MODE(
- StringRef, IPAMode, "ipa",
- "Controls the mode of inter-procedural analysis. Value: \"none\", "
- "\"basic-inlining\", \"inlining\", \"dynamic\", \"dynamic-bifurcate\".",
- /* SHALLOW_VAL */ "inlining", /* DEEP_VAL */ "dynamic-bifurcate")
-
ANALYZER_OPTION(
StringRef, ExplorationStrategy, "exploration_strategy",
"Value: \"dfs\", \"bfs\", \"unexplored_first\", "
@@ -393,5 +391,17 @@ ANALYZER_OPTION(
"\"bfs_block_dfs_contents\".",
"unexplored_first_queue")
+ANALYZER_OPTION(
+ StringRef, RawSilencedCheckersAndPackages, "silence-checkers",
+ "A semicolon separated list of checker and package names to silence. "
+ "Silenced checkers will not emit reports, but the modeling remain enabled.",
+ "")
+
+ANALYZER_OPTION_DEPENDS_ON_USER_MODE(
+ StringRef, IPAMode, "ipa",
+ "Controls the mode of inter-procedural analysis. Value: \"none\", "
+ "\"basic-inlining\", \"inlining\", \"dynamic\", \"dynamic-bifurcate\".",
+ /* SHALLOW_VAL */ "inlining", /* DEEP_VAL */ "dynamic-bifurcate")
+
#undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE
#undef ANALYZER_OPTION
diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
index 9630a229bd3b..ce16095e10c0 100644
--- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
+++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
@@ -163,9 +163,16 @@ class AnalyzerOptions : public RefCountedBase<AnalyzerOptions> {
public:
using ConfigTable = llvm::StringMap<std::string>;
+ /// Retrieves the list of checkers generated from Checkers.td. This doesn't
+ /// contain statically linked but non-generated checkers and plugin checkers!
static std::vector<StringRef>
getRegisteredCheckers(bool IncludeExperimental = false);
+ /// Retrieves the list of packages generated from Checkers.td. This doesn't
+ /// contain statically linked but non-generated packages and plugin packages!
+ static std::vector<StringRef>
+ getRegisteredPackages(bool IncludeExperimental = false);
+
/// Convenience function for printing options or checkers and their
/// description in a formatted manner. If \p MinLineWidth is set to 0, no line
/// breaks are introduced for the description.
@@ -188,9 +195,11 @@ public:
std::pair<StringRef, StringRef> EntryDescPair,
size_t EntryWidth, size_t InitialPad, size_t MinLineWidth = 0);
+ /// Pairs of checker/package name and enable/disable.
+ std::vector<std::pair<std::string, bool>> CheckersAndPackages;
- /// Pair of checker name and enable/disable.
- std::vector<std::pair<std::string, bool>> CheckersControlList;
+ /// Vector of checker/package names which will not emit warnings.
+ std::vector<std::string> SilencedCheckersAndPackages;
/// A key-value table of use-specified configuration values.
// TODO: This shouldn't be public.
@@ -212,12 +221,12 @@ public:
/// The maximum number of times the analyzer visits a block.
unsigned maxBlockVisitOnPath;
- /// Disable all analyzer checks.
+ /// Disable all analyzer checkers.
///
- /// This flag allows one to disable analyzer checks on the code processed by
+ /// This flag allows one to disable analyzer checkers on the code processed by
/// the given analysis consumer. Note, the code will get parsed and the
/// command-line options will get checked.
- unsigned DisableAllChecks : 1;
+ unsigned DisableAllCheckers : 1;
unsigned ShowCheckerHelp : 1;
unsigned ShowCheckerHelpAlpha : 1;
@@ -269,13 +278,13 @@ public:
// Create an array of all -analyzer-config command line options. Sort it in
// the constructor.
- std::vector<StringRef> AnalyzerConfigCmdFlags = {
+ std::vector<llvm::StringLiteral> AnalyzerConfigCmdFlags = {
#define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \
SHALLOW_VAL, DEEP_VAL) \
ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, SHALLOW_VAL)
#define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \
- CMDFLAG,
+ llvm::StringLiteral(CMDFLAG),
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.def"
#undef ANALYZER_OPTION
@@ -292,7 +301,7 @@ public:
}
AnalyzerOptions()
- : DisableAllChecks(false), ShowCheckerHelp(false),
+ : DisableAllCheckers(false), ShowCheckerHelp(false),
ShowCheckerHelpAlpha(false), ShowCheckerHelpDeveloper(false),
ShowCheckerOptionList(false), ShowCheckerOptionAlphaList(false),
ShowCheckerOptionDeveloperList(false), ShowEnabledCheckerList(false),
@@ -310,7 +319,7 @@ public:
/// If an option value is not provided, returns the given \p DefaultVal.
/// @param [in] CheckerName The *full name* of the checker. One may retrieve
/// this from the checker object's field \c Name, or through \c
- /// CheckerManager::getCurrentCheckName within the checker's registry
+ /// CheckerManager::getCurrentCheckerName within the checker's registry
/// function.
/// Checker options are retrieved in the following format:
/// `-analyzer-config CheckerName:OptionName=Value.
@@ -330,7 +339,7 @@ public:
/// If an option value is not provided, returns the given \p DefaultVal.
/// @param [in] CheckerName The *full name* of the checker. One may retrieve
/// this from the checker object's field \c Name, or through \c
- /// CheckerManager::getCurrentCheckName within the checker's registry
+ /// CheckerManager::getCurrentCheckerName within the checker's registry
/// function.
/// Checker options are retrieved in the following format:
/// `-analyzer-config CheckerName:OptionName=Value.
@@ -350,7 +359,7 @@ public:
/// If an option value is not provided, returns the given \p DefaultVal.
/// @param [in] CheckerName The *full name* of the checker. One may retrieve
/// this from the checker object's field \c Name, or through \c
- /// CheckerManager::getCurrentCheckName within the checker's registry
+ /// CheckerManager::getCurrentCheckerName within the checker's registry
/// function.
/// Checker options are retrieved in the following format:
/// `-analyzer-config CheckerName:OptionName=Value.
@@ -404,6 +413,43 @@ inline UserModeKind AnalyzerOptions::getUserMode() const {
return K.getValue();
}
+inline std::vector<StringRef>
+AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental) {
+ static constexpr llvm::StringLiteral StaticAnalyzerCheckerNames[] = {
+#define GET_CHECKERS
+#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
+ llvm::StringLiteral(FULLNAME),
+#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
+#undef CHECKER
+#undef GET_CHECKERS
+ };
+ std::vector<StringRef> Checkers;
+ for (StringRef CheckerName : StaticAnalyzerCheckerNames) {
+ if (!CheckerName.startswith("debug.") &&
+ (IncludeExperimental || !CheckerName.startswith("alpha.")))
+ Checkers.push_back(CheckerName);
+ }
+ return Checkers;
+}
+
+inline std::vector<StringRef>
+AnalyzerOptions::getRegisteredPackages(bool IncludeExperimental) {
+ static constexpr llvm::StringLiteral StaticAnalyzerPackageNames[] = {
+#define GET_PACKAGES
+#define PACKAGE(FULLNAME) llvm::StringLiteral(FULLNAME),
+#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
+#undef PACKAGE
+#undef GET_PACKAGES
+ };
+ std::vector<StringRef> Packages;
+ for (StringRef PackageName : StaticAnalyzerPackageNames) {
+ if (PackageName != "debug" &&
+ (IncludeExperimental || PackageName != "alpha"))
+ Packages.push_back(PackageName);
+ }
+ return Packages;
+}
+
} // namespace clang
#endif // LLVM_CLANG_STATICANALYZER_CORE_ANALYZEROPTIONS_H
diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h
index d30ad19b20f8..e94b544172a0 100644
--- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h
+++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h
@@ -14,10 +14,10 @@
#ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H
#define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTER_H
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
-#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
@@ -70,59 +70,253 @@ class SValBuilder;
using DiagnosticForConsumerMapTy =
llvm::DenseMap<PathDiagnosticConsumer *, std::unique_ptr<PathDiagnostic>>;
-/// This class provides an interface through which checkers can create
-/// individual bug reports.
-class BugReport : public llvm::ilist_node<BugReport> {
+/// Interface for classes constructing Stack hints.
+///
+/// If a PathDiagnosticEvent occurs in a different frame than the final
+/// diagnostic the hints can be used to summarize the effect of the call.
+class StackHintGenerator {
public:
- class NodeResolver {
- virtual void anchor();
+ virtual ~StackHintGenerator() = 0;
- public:
- virtual ~NodeResolver() = default;
+ /// Construct the Diagnostic message for the given ExplodedNode.
+ virtual std::string getMessage(const ExplodedNode *N) = 0;
+};
- virtual const ExplodedNode*
- getOriginalNode(const ExplodedNode *N) = 0;
- };
+/// Constructs a Stack hint for the given symbol.
+///
+/// The class knows how to construct the stack hint message based on
+/// traversing the CallExpr associated with the call and checking if the given
+/// symbol is returned or is one of the arguments.
+/// The hint can be customized by redefining 'getMessageForX()' methods.
+class StackHintGeneratorForSymbol : public StackHintGenerator {
+private:
+ SymbolRef Sym;
+ std::string Msg;
- using ranges_iterator = const SourceRange *;
- using VisitorList = SmallVector<std::unique_ptr<BugReporterVisitor>, 8>;
- using visitor_iterator = VisitorList::iterator;
- using ExtraTextList = SmallVector<StringRef, 2>;
- using NoteList = SmallVector<std::shared_ptr<PathDiagnosticNotePiece>, 4>;
+public:
+ StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {}
+ ~StackHintGeneratorForSymbol() override = default;
+
+ /// Search the call expression for the symbol Sym and dispatch the
+ /// 'getMessageForX()' methods to construct a specific message.
+ std::string getMessage(const ExplodedNode *N) override;
+
+ /// Produces the message of the following form:
+ /// 'Msg via Nth parameter'
+ virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex);
+
+ virtual std::string getMessageForReturn(const CallExpr *CallExpr) {
+ return Msg;
+ }
+
+ virtual std::string getMessageForSymbolNotFound() {
+ return Msg;
+ }
+};
+
+/// This class provides an interface through which checkers can create
+/// individual bug reports.
+class BugReport {
+public:
+ enum class Kind { Basic, PathSensitive };
protected:
friend class BugReportEquivClass;
friend class BugReporter;
+ Kind K;
const BugType& BT;
- const Decl *DeclWithIssue = nullptr;
std::string ShortDescription;
std::string Description;
+
+ SmallVector<SourceRange, 4> Ranges;
+ SmallVector<std::shared_ptr<PathDiagnosticNotePiece>, 4> Notes;
+ SmallVector<FixItHint, 4> Fixits;
+
+ BugReport(Kind kind, const BugType &bt, StringRef desc)
+ : K(kind), BT(bt), Description(desc) {}
+
+ BugReport(Kind K, const BugType &BT, StringRef ShortDescription,
+ StringRef Description)
+ : K(K), BT(BT), ShortDescription(ShortDescription),
+ Description(Description) {}
+
+public:
+ virtual ~BugReport() = default;
+
+ Kind getKind() const { return K; }
+
+ const BugType& getBugType() const { return BT; }
+
+ /// A verbose warning message that is appropriate for displaying next to
+ /// the source code that introduces the problem. The description should be
+ /// at least a full sentence starting with a capital letter. The period at
+ /// the end of the warning is traditionally omitted. If the description
+ /// consists of multiple sentences, periods between the sentences are
+ /// encouraged, but the period at the end of the description is still omitted.
+ StringRef getDescription() const { return Description; }
+
+ /// A short general warning message that is appropriate for displaying in
+ /// the list of all reported bugs. It should describe what kind of bug is found
+ /// but does not need to try to go into details of that specific bug.
+ /// Grammatical conventions of getDescription() apply here as well.
+ StringRef getShortDescription(bool UseFallback = true) const {
+ if (ShortDescription.empty() && UseFallback)
+ return Description;
+ return ShortDescription;
+ }
+
+ /// The primary location of the bug report that points at the undesirable
+ /// behavior in the code. UIs should attach the warning description to this
+ /// location. The warning description should describe the bad behavior
+ /// at this location.
+ virtual PathDiagnosticLocation getLocation() const = 0;
+
+ /// The smallest declaration that contains the bug location.
+ /// This is purely cosmetic; the declaration can be displayed to the user
+ /// but it does not affect whether the report is emitted.
+ virtual const Decl *getDeclWithIssue() const = 0;
+
+ /// Get the location on which the report should be uniqued. Two warnings are
+ /// considered to be equivalent whenever they have the same bug types,
+ /// descriptions, and uniqueing locations. Out of a class of equivalent
+ /// warnings only one gets displayed to the user. For most warnings the
+ /// uniqueing location coincides with their location, but sometimes
+ /// it makes sense to use different locations. For example, a leak
+ /// checker can place the warning at the location where the last reference
+ /// to the leaking resource is dropped but at the same time unique the warning
+ /// by where that resource is acquired (allocated).
+ virtual PathDiagnosticLocation getUniqueingLocation() const = 0;
+
+ /// Get the declaration that corresponds to (usually contains) the uniqueing
+ /// location. This is not actively used for uniqueing, i.e. otherwise
+ /// identical reports that have different uniqueing decls will be considered
+ /// equivalent.
+ virtual const Decl *getUniqueingDecl() const = 0;
+
+ /// Add new item to the list of additional notes that need to be attached to
+ /// this report. If the report is path-sensitive, these notes will not be
+ /// displayed as part of the execution path explanation, but will be displayed
+ /// separately. Use bug visitors if you need to add an extra path note.
+ void addNote(StringRef Msg, const PathDiagnosticLocation &Pos,
+ ArrayRef<SourceRange> Ranges = {}) {
+ auto P = std::make_shared<PathDiagnosticNotePiece>(Pos, Msg);
+
+ for (const auto &R : Ranges)
+ P->addRange(R);
+
+ Notes.push_back(std::move(P));
+ }
+
+ ArrayRef<std::shared_ptr<PathDiagnosticNotePiece>> getNotes() {
+ return Notes;
+ }
+
+ /// Add a range to a bug report.
+ ///
+ /// Ranges are used to highlight regions of interest in the source code.
+ /// They should be at the same source code line as the BugReport location.
+ /// By default, the source range of the statement corresponding to the error
+ /// node will be used; add a single invalid range to specify absence of
+ /// ranges.
+ void addRange(SourceRange R) {
+ assert((R.isValid() || Ranges.empty()) && "Invalid range can only be used "
+ "to specify that the report does not have a range.");
+ Ranges.push_back(R);
+ }
+
+ /// Get the SourceRanges associated with the report.
+ virtual ArrayRef<SourceRange> getRanges() const {
+ return Ranges;
+ }
+
+ /// Add a fix-it hint to the bug report.
+ ///
+ /// Fix-it hints are the suggested edits to the code that would resolve
+ /// the problem explained by the bug report. Fix-it hints should be
+ /// as conservative as possible because it is not uncommon for the user
+ /// to blindly apply all fixits to their project. Note that it is very hard
+ /// to produce a good fix-it hint for most path-sensitive warnings.
+ void addFixItHint(const FixItHint &F) {
+ Fixits.push_back(F);
+ }
+
+ llvm::ArrayRef<FixItHint> getFixits() const { return Fixits; }
+
+ /// Reports are uniqued to ensure that we do not emit multiple diagnostics
+ /// for each bug.
+ virtual void Profile(llvm::FoldingSetNodeID& hash) const = 0;
+};
+
+class BasicBugReport : public BugReport {
PathDiagnosticLocation Location;
- PathDiagnosticLocation UniqueingLocation;
- const Decl *UniqueingDecl;
+ const Decl *DeclWithIssue = nullptr;
+
+public:
+ BasicBugReport(const BugType &bt, StringRef desc, PathDiagnosticLocation l)
+ : BugReport(Kind::Basic, bt, desc), Location(l) {}
+
+ static bool classof(const BugReport *R) {
+ return R->getKind() == Kind::Basic;
+ }
+
+ PathDiagnosticLocation getLocation() const override {
+ assert(Location.isValid());
+ return Location;
+ }
+
+ const Decl *getDeclWithIssue() const override {
+ return DeclWithIssue;
+ }
+
+ PathDiagnosticLocation getUniqueingLocation() const override {
+ return getLocation();
+ }
+
+ const Decl *getUniqueingDecl() const override {
+ return getDeclWithIssue();
+ }
+
+ /// Specifically set the Decl where an issue occurred. This isn't necessary
+ /// for BugReports that cover a path as it will be automatically inferred.
+ void setDeclWithIssue(const Decl *declWithIssue) {
+ DeclWithIssue = declWithIssue;
+ }
+ void Profile(llvm::FoldingSetNodeID& hash) const override;
+};
+
+class PathSensitiveBugReport : public BugReport {
+public:
+ using VisitorList = SmallVector<std::unique_ptr<BugReporterVisitor>, 8>;
+ using visitor_iterator = VisitorList::iterator;
+ using visitor_range = llvm::iterator_range<visitor_iterator>;
+
+protected:
+ /// The ExplodedGraph node against which the report was thrown. It corresponds
+ /// to the end of the execution path that demonstrates the bug.
const ExplodedNode *ErrorNode = nullptr;
- SmallVector<SourceRange, 4> Ranges;
- ExtraTextList ExtraText;
- NoteList Notes;
- using Symbols = llvm::DenseSet<SymbolRef>;
- using Regions = llvm::DenseSet<const MemRegion *>;
+ /// The range that corresponds to ErrorNode's program point. It is usually
+ /// highlighted in the report.
+ const SourceRange ErrorNodeRange;
+
+ /// Profile to identify equivalent bug reports for error report coalescing.
/// A (stack of) a set of symbols that are registered with this
/// report as being "interesting", and thus used to help decide which
/// diagnostics to include when constructing the final path diagnostic.
/// The stack is largely used by BugReporter when generating PathDiagnostics
/// for multiple PathDiagnosticConsumers.
- SmallVector<Symbols *, 2> interestingSymbols;
+ llvm::DenseMap<SymbolRef, bugreporter::TrackingKind> InterestingSymbols;
/// A (stack of) set of regions that are registered with this report as being
/// "interesting", and thus used to help decide which diagnostics
/// to include when constructing the final path diagnostic.
/// The stack is largely used by BugReporter when generating PathDiagnostics
/// for multiple PathDiagnosticConsumers.
- SmallVector<Regions *, 2> interestingRegions;
+ llvm::DenseMap<const MemRegion *, bugreporter::TrackingKind>
+ InterestingRegions;
/// A set of location contexts that correspoind to call sites which should be
/// considered "interesting".
@@ -156,66 +350,58 @@ protected:
/// Conditions we're already tracking.
llvm::SmallSet<const ExplodedNode *, 4> TrackedConditions;
-private:
- // Used internally by BugReporter.
- Symbols &getInterestingSymbols();
- Regions &getInterestingRegions();
-
- void lazyInitializeInterestingSets();
- void pushInterestingSymbolsAndRegions();
- void popInterestingSymbolsAndRegions();
-
-public:
- BugReport(const BugType& bt, StringRef desc, const ExplodedNode *errornode)
- : BT(bt), Description(desc), ErrorNode(errornode) {}
+ /// Reports with different uniqueing locations are considered to be different
+ /// for the purposes of deduplication.
+ PathDiagnosticLocation UniqueingLocation;
+ const Decl *UniqueingDecl;
- BugReport(const BugType& bt, StringRef shortDesc, StringRef desc,
- const ExplodedNode *errornode)
- : BT(bt), ShortDescription(shortDesc), Description(desc),
- ErrorNode(errornode) {}
+ const Stmt *getStmt() const;
- BugReport(const BugType &bt, StringRef desc, PathDiagnosticLocation l)
- : BT(bt), Description(desc), Location(l) {}
+ /// If an event occurs in a different frame than the final diagnostic,
+ /// supply a message that will be used to construct an extra hint on the
+ /// returns from all the calls on the stack from this event to the final
+ /// diagnostic.
+ // FIXME: Allow shared_ptr keys in DenseMap?
+ std::map<PathDiagnosticPieceRef, std::unique_ptr<StackHintGenerator>>
+ StackHints;
- /// Create a BugReport with a custom uniqueing location.
+public:
+ PathSensitiveBugReport(const BugType &bt, StringRef desc,
+ const ExplodedNode *errorNode)
+ : BugReport(Kind::PathSensitive, bt, desc), ErrorNode(errorNode),
+ ErrorNodeRange(getStmt() ? getStmt()->getSourceRange()
+ : SourceRange()) {}
+
+ PathSensitiveBugReport(const BugType &bt, StringRef shortDesc, StringRef desc,
+ const ExplodedNode *errorNode)
+ : BugReport(Kind::PathSensitive, bt, shortDesc, desc),
+ ErrorNode(errorNode),
+ ErrorNodeRange(getStmt() ? getStmt()->getSourceRange()
+ : SourceRange()) {}
+
+ /// Create a PathSensitiveBugReport with a custom uniqueing location.
///
/// The reports that have the same report location, description, bug type, and
/// ranges are uniqued - only one of the equivalent reports will be presented
/// to the user. This method allows to rest the location which should be used
/// for uniquing reports. For example, memory leaks checker, could set this to
/// the allocation site, rather then the location where the bug is reported.
- BugReport(BugType& bt, StringRef desc, const ExplodedNode *errornode,
- PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique)
- : BT(bt), Description(desc), UniqueingLocation(LocationToUnique),
- UniqueingDecl(DeclToUnique), ErrorNode(errornode) {}
-
- virtual ~BugReport();
-
- const BugType& getBugType() const { return BT; }
- //BugType& getBugType() { return BT; }
+ PathSensitiveBugReport(BugType &bt, StringRef desc,
+ const ExplodedNode *errorNode,
+ PathDiagnosticLocation LocationToUnique,
+ const Decl *DeclToUnique)
+ : BugReport(Kind::PathSensitive, bt, desc), ErrorNode(errorNode),
+ ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() : SourceRange()),
+ UniqueingLocation(LocationToUnique), UniqueingDecl(DeclToUnique) {
+ assert(errorNode);
+ }
- /// True when the report has an execution path associated with it.
- ///
- /// A report is said to be path-sensitive if it was thrown against a
- /// particular exploded node in the path-sensitive analysis graph.
- /// Path-sensitive reports have their intermediate path diagnostics
- /// auto-generated, perhaps with the help of checker-defined visitors,
- /// and may contain extra notes.
- /// Path-insensitive reports consist only of a single warning message
- /// in a specific location, and perhaps extra notes.
- /// Path-sensitive checkers are allowed to throw path-insensitive reports.
- bool isPathSensitive() const { return ErrorNode != nullptr; }
+ static bool classof(const BugReport *R) {
+ return R->getKind() == Kind::PathSensitive;
+ }
const ExplodedNode *getErrorNode() const { return ErrorNode; }
- StringRef getDescription() const { return Description; }
-
- StringRef getShortDescription(bool UseFallback = true) const {
- if (ShortDescription.empty() && UseFallback)
- return Description;
- return ShortDescription;
- }
-
/// Indicates whether or not any path pruning should take place
/// when generating a PathDiagnostic from this BugReport.
bool shouldPrunePath() const { return !DoNotPrunePath; }
@@ -223,15 +409,54 @@ public:
/// Disable all path pruning when generating a PathDiagnostic.
void disablePathPruning() { DoNotPrunePath = true; }
- void markInteresting(SymbolRef sym);
- void markInteresting(const MemRegion *R);
- void markInteresting(SVal V);
+ /// Get the location on which the report should be uniqued.
+ PathDiagnosticLocation getUniqueingLocation() const override {
+ return UniqueingLocation;
+ }
+
+ /// Get the declaration containing the uniqueing location.
+ const Decl *getUniqueingDecl() const override {
+ return UniqueingDecl;
+ }
+
+ const Decl *getDeclWithIssue() const override;
+
+ ArrayRef<SourceRange> getRanges() const override;
+
+ PathDiagnosticLocation getLocation() const override;
+
+ /// Marks a symbol as interesting. Different kinds of interestingness will
+ /// be processed differently by visitors (e.g. if the tracking kind is
+ /// condition, will append "will be used as a condition" to the message).
+ void markInteresting(SymbolRef sym, bugreporter::TrackingKind TKind =
+ bugreporter::TrackingKind::Thorough);
+
+ /// Marks a region as interesting. Different kinds of interestingness will
+ /// be processed differently by visitors (e.g. if the tracking kind is
+ /// condition, will append "will be used as a condition" to the message).
+ void markInteresting(
+ const MemRegion *R,
+ bugreporter::TrackingKind TKind = bugreporter::TrackingKind::Thorough);
+
+ /// Marks a symbolic value as interesting. Different kinds of interestingness
+ /// will be processed differently by visitors (e.g. if the tracking kind is
+ /// condition, will append "will be used as a condition" to the message).
+ void markInteresting(SVal V, bugreporter::TrackingKind TKind =
+ bugreporter::TrackingKind::Thorough);
void markInteresting(const LocationContext *LC);
- bool isInteresting(SymbolRef sym);
- bool isInteresting(const MemRegion *R);
- bool isInteresting(SVal V);
- bool isInteresting(const LocationContext *LC);
+ bool isInteresting(SymbolRef sym) const;
+ bool isInteresting(const MemRegion *R) const;
+ bool isInteresting(SVal V) const;
+ bool isInteresting(const LocationContext *LC) const;
+
+ Optional<bugreporter::TrackingKind>
+ getInterestingnessKind(SymbolRef sym) const;
+
+ Optional<bugreporter::TrackingKind>
+ getInterestingnessKind(const MemRegion *R) const;
+
+ Optional<bugreporter::TrackingKind> getInterestingnessKind(SVal V) const;
/// Returns whether or not this report should be considered valid.
///
@@ -254,87 +479,10 @@ public:
Invalidations.insert(std::make_pair(Tag, Data));
}
- /// Return the canonical declaration, be it a method or class, where
- /// this issue semantically occurred.
- const Decl *getDeclWithIssue() const;
-
- /// Specifically set the Decl where an issue occurred. This isn't necessary
- /// for BugReports that cover a path as it will be automatically inferred.
- void setDeclWithIssue(const Decl *declWithIssue) {
- DeclWithIssue = declWithIssue;
- }
-
- /// Add new item to the list of additional notes that need to be attached to
- /// this path-insensitive report. If you want to add extra notes to a
- /// path-sensitive report, you need to use a BugReporterVisitor because it
- /// allows you to specify where exactly in the auto-generated path diagnostic
- /// the extra note should appear.
- void addNote(StringRef Msg, const PathDiagnosticLocation &Pos,
- ArrayRef<SourceRange> Ranges) {
- auto P = std::make_shared<PathDiagnosticNotePiece>(Pos, Msg);
-
- for (const auto &R : Ranges)
- P->addRange(R);
-
- Notes.push_back(std::move(P));
- }
-
- // FIXME: Instead of making an override, we could have default-initialized
- // Ranges with {}, however it crashes the MSVC 2013 compiler.
- void addNote(StringRef Msg, const PathDiagnosticLocation &Pos) {
- std::vector<SourceRange> Ranges;
- addNote(Msg, Pos, Ranges);
- }
-
- virtual const NoteList &getNotes() {
- return Notes;
- }
-
- /// This allows for addition of meta data to the diagnostic.
- ///
- /// Currently, only the HTMLDiagnosticClient knows how to display it.
- void addExtraText(StringRef S) {
- ExtraText.push_back(S);
- }
-
- virtual const ExtraTextList &getExtraText() {
- return ExtraText;
- }
-
- /// Return the "definitive" location of the reported bug.
- ///
- /// While a bug can span an entire path, usually there is a specific
- /// location that can be used to identify where the key issue occurred.
- /// This location is used by clients rendering diagnostics.
- virtual PathDiagnosticLocation getLocation(const SourceManager &SM) const;
-
- /// Get the location on which the report should be uniqued.
- PathDiagnosticLocation getUniqueingLocation() const {
- return UniqueingLocation;
- }
-
- /// Get the declaration containing the uniqueing location.
- const Decl *getUniqueingDecl() const {
- return UniqueingDecl;
- }
-
- const Stmt *getStmt() const;
-
- /// Add a range to a bug report.
- ///
- /// Ranges are used to highlight regions of interest in the source code.
- /// They should be at the same source code line as the BugReport location.
- /// By default, the source range of the statement corresponding to the error
- /// node will be used; add a single invalid range to specify absence of
- /// ranges.
- void addRange(SourceRange R) {
- assert((R.isValid() || Ranges.empty()) && "Invalid range can only be used "
- "to specify that the report does not have a range.");
- Ranges.push_back(R);
- }
-
- /// Get the SourceRanges associated with the report.
- virtual llvm::iterator_range<ranges_iterator> getRanges();
+ /// Profile to identify equivalent bug reports for error report coalescing.
+ /// Reports are uniqued to ensure that we do not emit multiple diagnostics
+ /// for each bug.
+ void Profile(llvm::FoldingSetNodeID &hash) const override;
/// Add custom or predefined bug report visitors to this report.
///
@@ -351,6 +499,7 @@ public:
/// Iterators through the custom diagnostic visitors.
visitor_iterator visitor_begin() { return Callbacks.begin(); }
visitor_iterator visitor_end() { return Callbacks.end(); }
+ visitor_range visitors() { return {visitor_begin(), visitor_end()}; }
/// Notes that the condition of the CFGBlock associated with \p Cond is
/// being tracked.
@@ -359,10 +508,25 @@ public:
return TrackedConditions.insert(Cond).second;
}
- /// Profile to identify equivalent bug reports for error report coalescing.
- /// Reports are uniqued to ensure that we do not emit multiple diagnostics
- /// for each bug.
- virtual void Profile(llvm::FoldingSetNodeID& hash) const;
+ void addCallStackHint(PathDiagnosticPieceRef Piece,
+ std::unique_ptr<StackHintGenerator> StackHint) {
+ StackHints[Piece] = std::move(StackHint);
+ }
+
+ bool hasCallStackHint(PathDiagnosticPieceRef Piece) const {
+ return StackHints.count(Piece) > 0;
+ }
+
+ /// Produce the hint for the given node. The node contains
+ /// information about the call for which the diagnostic can be generated.
+ std::string
+ getCallStackMessage(PathDiagnosticPieceRef Piece,
+ const ExplodedNode *N) const {
+ auto I = StackHints.find(Piece);
+ if (I != StackHints.end())
+ return I->second->getMessage(N);
+ return "";
+ }
};
//===----------------------------------------------------------------------===//
@@ -373,29 +537,21 @@ class BugReportEquivClass : public llvm::FoldingSetNode {
friend class BugReporter;
/// List of *owned* BugReport objects.
- llvm::ilist<BugReport> Reports;
+ llvm::SmallVector<std::unique_ptr<BugReport>, 4> Reports;
- void AddReport(std::unique_ptr<BugReport> R) {
- Reports.push_back(R.release());
+ void AddReport(std::unique_ptr<BugReport> &&R) {
+ Reports.push_back(std::move(R));
}
public:
BugReportEquivClass(std::unique_ptr<BugReport> R) { AddReport(std::move(R)); }
- ~BugReportEquivClass();
+
+ ArrayRef<std::unique_ptr<BugReport>> getReports() const { return Reports; }
void Profile(llvm::FoldingSetNodeID& ID) const {
assert(!Reports.empty());
- Reports.front().Profile(ID);
+ Reports.front()->Profile(ID);
}
-
- using iterator = llvm::ilist<BugReport>::iterator;
- using const_iterator = llvm::ilist<BugReport>::const_iterator;
-
- iterator begin() { return Reports.begin(); }
- iterator end() { return Reports.end(); }
-
- const_iterator begin() const { return Reports.begin(); }
- const_iterator end() const { return Reports.end(); }
};
//===----------------------------------------------------------------------===//
@@ -404,9 +560,8 @@ public:
class BugReporterData {
public:
- virtual ~BugReporterData();
+ virtual ~BugReporterData() = default;
- virtual DiagnosticsEngine& getDiagnostic() = 0;
virtual ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() = 0;
virtual ASTContext &getASTContext() = 0;
virtual SourceManager &getSourceManager() = 0;
@@ -419,60 +574,29 @@ public:
///
/// The base class is used for generating path-insensitive
class BugReporter {
-public:
- enum Kind { BaseBRKind, GRBugReporterKind };
-
private:
- using BugTypesTy = llvm::ImmutableSet<BugType *>;
-
- BugTypesTy::Factory F;
- BugTypesTy BugTypes;
-
- const Kind kind;
BugReporterData& D;
/// Generate and flush the diagnostics for the given bug report.
void FlushReport(BugReportEquivClass& EQ);
- /// Generate the diagnostics for the given bug report.
- std::unique_ptr<DiagnosticForConsumerMapTy>
- generateDiagnosticForConsumerMap(BugReport *exampleReport,
- ArrayRef<PathDiagnosticConsumer *> consumers,
- ArrayRef<BugReport *> bugReports);
-
/// The set of bug reports tracked by the BugReporter.
llvm::FoldingSet<BugReportEquivClass> EQClasses;
/// A vector of BugReports for tracking the allocated pointers and cleanup.
std::vector<BugReportEquivClass *> EQClassesVector;
-protected:
- BugReporter(BugReporterData& d, Kind k)
- : BugTypes(F.getEmptySet()), kind(k), D(d) {}
-
public:
- BugReporter(BugReporterData& d)
- : BugTypes(F.getEmptySet()), kind(BaseBRKind), D(d) {}
+ BugReporter(BugReporterData &d) : D(d) {}
virtual ~BugReporter();
/// Generate and flush diagnostics for all bug reports.
void FlushReports();
- Kind getKind() const { return kind; }
-
- DiagnosticsEngine& getDiagnostic() {
- return D.getDiagnostic();
- }
-
ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() {
return D.getPathDiagnosticConsumers();
}
- /// Iterator over the set of BugTypes tracked by the BugReporter.
- using iterator = BugTypesTy::iterator;
- iterator begin() { return BugTypes.begin(); }
- iterator end() { return BugTypes.end(); }
-
/// Iterator over the set of BugReports tracked by the BugReporter.
using EQClasses_iterator = llvm::FoldingSet<BugReportEquivClass>::iterator;
EQClasses_iterator EQClasses_begin() { return EQClasses.begin(); }
@@ -480,126 +604,116 @@ public:
ASTContext &getContext() { return D.getASTContext(); }
- SourceManager &getSourceManager() { return D.getSourceManager(); }
-
- AnalyzerOptions &getAnalyzerOptions() { return D.getAnalyzerOptions(); }
-
- virtual std::unique_ptr<DiagnosticForConsumerMapTy>
- generatePathDiagnostics(ArrayRef<PathDiagnosticConsumer *> consumers,
- ArrayRef<BugReport *> &bugReports) {
- return {};
- }
+ const SourceManager &getSourceManager() { return D.getSourceManager(); }
- void Register(const BugType *BT);
+ const AnalyzerOptions &getAnalyzerOptions() { return D.getAnalyzerOptions(); }
/// Add the given report to the set of reports tracked by BugReporter.
///
/// The reports are usually generated by the checkers. Further, they are
/// folded based on the profile value, which is done to coalesce similar
/// reports.
- void emitReport(std::unique_ptr<BugReport> R);
+ virtual void emitReport(std::unique_ptr<BugReport> R);
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker,
StringRef BugName, StringRef BugCategory,
StringRef BugStr, PathDiagnosticLocation Loc,
- ArrayRef<SourceRange> Ranges = None);
+ ArrayRef<SourceRange> Ranges = None,
+ ArrayRef<FixItHint> Fixits = None);
- void EmitBasicReport(const Decl *DeclWithIssue, CheckName CheckName,
+ void EmitBasicReport(const Decl *DeclWithIssue, CheckerNameRef CheckerName,
StringRef BugName, StringRef BugCategory,
StringRef BugStr, PathDiagnosticLocation Loc,
- ArrayRef<SourceRange> Ranges = None);
+ ArrayRef<SourceRange> Ranges = None,
+ ArrayRef<FixItHint> Fixits = None);
private:
llvm::StringMap<BugType *> StrBugTypes;
/// Returns a BugType that is associated with the given name and
/// category.
- BugType *getBugTypeForName(CheckName CheckName, StringRef name,
+ BugType *getBugTypeForName(CheckerNameRef CheckerName, StringRef name,
StringRef category);
+
+ virtual BugReport *
+ findReportInEquivalenceClass(BugReportEquivClass &eqClass,
+ SmallVectorImpl<BugReport *> &bugReports) {
+ return eqClass.getReports()[0].get();
+ }
+
+protected:
+ /// Generate the diagnostics for the given bug report.
+ virtual std::unique_ptr<DiagnosticForConsumerMapTy>
+ generateDiagnosticForConsumerMap(BugReport *exampleReport,
+ ArrayRef<PathDiagnosticConsumer *> consumers,
+ ArrayRef<BugReport *> bugReports);
};
/// GRBugReporter is used for generating path-sensitive reports.
-class GRBugReporter : public BugReporter {
+class PathSensitiveBugReporter final : public BugReporter {
ExprEngine& Eng;
-public:
- GRBugReporter(BugReporterData& d, ExprEngine& eng)
- : BugReporter(d, GRBugReporterKind), Eng(eng) {}
+ BugReport *findReportInEquivalenceClass(
+ BugReportEquivClass &eqClass,
+ SmallVectorImpl<BugReport *> &bugReports) override;
- ~GRBugReporter() override;
+ /// Generate the diagnostics for the given bug report.
+ std::unique_ptr<DiagnosticForConsumerMapTy>
+ generateDiagnosticForConsumerMap(BugReport *exampleReport,
+ ArrayRef<PathDiagnosticConsumer *> consumers,
+ ArrayRef<BugReport *> bugReports) override;
+public:
+ PathSensitiveBugReporter(BugReporterData& d, ExprEngine& eng)
+ : BugReporter(d), Eng(eng) {}
/// getGraph - Get the exploded graph created by the analysis engine
/// for the analyzed method or function.
- ExplodedGraph &getGraph();
+ const ExplodedGraph &getGraph() const;
/// getStateManager - Return the state manager used by the analysis
/// engine.
- ProgramStateManager &getStateManager();
+ ProgramStateManager &getStateManager() const;
/// \p bugReports A set of bug reports within a *single* equivalence class
///
/// \return A mapping from consumers to the corresponding diagnostics.
/// Iterates through the bug reports within a single equivalence class,
/// stops at a first non-invalidated report.
- std::unique_ptr<DiagnosticForConsumerMapTy>
- generatePathDiagnostics(ArrayRef<PathDiagnosticConsumer *> consumers,
- ArrayRef<BugReport *> &bugReports) override;
+ std::unique_ptr<DiagnosticForConsumerMapTy> generatePathDiagnostics(
+ ArrayRef<PathDiagnosticConsumer *> consumers,
+ ArrayRef<PathSensitiveBugReport *> &bugReports);
- /// classof - Used by isa<>, cast<>, and dyn_cast<>.
- static bool classof(const BugReporter* R) {
- return R->getKind() == GRBugReporterKind;
- }
+ void emitReport(std::unique_ptr<BugReport> R) override;
};
-class NodeMapClosure : public BugReport::NodeResolver {
- InterExplodedGraphMap &M;
-
-public:
- NodeMapClosure(InterExplodedGraphMap &m) : M(m) {}
-
- const ExplodedNode *getOriginalNode(const ExplodedNode *N) override {
- return M.lookup(N);
- }
-};
-
class BugReporterContext {
- GRBugReporter &BR;
- NodeMapClosure NMC;
+ PathSensitiveBugReporter &BR;
virtual void anchor();
public:
- BugReporterContext(GRBugReporter &br, InterExplodedGraphMap &Backmap)
- : BR(br), NMC(Backmap) {}
+ BugReporterContext(PathSensitiveBugReporter &br) : BR(br) {}
virtual ~BugReporterContext() = default;
- GRBugReporter& getBugReporter() { return BR; }
+ PathSensitiveBugReporter& getBugReporter() { return BR; }
- ExplodedGraph &getGraph() { return BR.getGraph(); }
-
- ProgramStateManager& getStateManager() {
+ ProgramStateManager& getStateManager() const {
return BR.getStateManager();
}
- SValBuilder &getSValBuilder() {
- return getStateManager().getSValBuilder();
- }
-
- ASTContext &getASTContext() {
+ ASTContext &getASTContext() const {
return BR.getContext();
}
- SourceManager& getSourceManager() {
+ const SourceManager& getSourceManager() const {
return BR.getSourceManager();
}
- AnalyzerOptions &getAnalyzerOptions() {
+ const AnalyzerOptions &getAnalyzerOptions() const {
return BR.getAnalyzerOptions();
}
-
- NodeMapClosure& getNodeResolver() { return NMC; }
};
@@ -648,7 +762,7 @@ public:
public:
const NoteTag *makeNoteTag(Callback &&Cb, bool IsPrunable = false) {
- // We cannot use make_unique because we cannot access the private
+ // We cannot use std::make_unique because we cannot access the private
// constructor from inside it.
std::unique_ptr<NoteTag> T(new NoteTag(std::move(Cb), IsPrunable));
Tags.push_back(std::move(T));
diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h
index ef5d327d39da..de0ee5de81b5 100644
--- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h
+++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h
@@ -33,11 +33,12 @@ class Stmt;
namespace ento {
-class BugReport;
+class PathSensitiveBugReport;
class BugReporterContext;
class ExplodedNode;
class MemRegion;
class PathDiagnosticPiece;
+using PathDiagnosticPieceRef = std::shared_ptr<PathDiagnosticPiece>;
/// BugReporterVisitors are used to add custom diagnostics along a path.
class BugReporterVisitor : public llvm::FoldingSetNode {
@@ -57,32 +58,68 @@ public:
///
/// The last parameter can be used to register a new visitor with the given
/// BugReport while processing a node.
- virtual std::shared_ptr<PathDiagnosticPiece>
- VisitNode(const ExplodedNode *Succ,
- BugReporterContext &BRC, BugReport &BR) = 0;
+ virtual PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) = 0;
/// Last function called on the visitor, no further calls to VisitNode
/// would follow.
virtual void finalizeVisitor(BugReporterContext &BRC,
const ExplodedNode *EndPathNode,
- BugReport &BR);
+ PathSensitiveBugReport &BR);
/// Provide custom definition for the final diagnostic piece on the
/// path - the piece, which is displayed before the path is expanded.
///
/// NOTE that this function can be implemented on at most one used visitor,
/// and otherwise it crahes at runtime.
- virtual std::shared_ptr<PathDiagnosticPiece>
- getEndPath(BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR);
+ virtual PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,
+ const ExplodedNode *N,
+ PathSensitiveBugReport &BR);
virtual void Profile(llvm::FoldingSetNodeID &ID) const = 0;
/// Generates the default final diagnostic piece.
- static std::shared_ptr<PathDiagnosticPiece>
- getDefaultEndPath(BugReporterContext &BRC, const ExplodedNode *N,
- BugReport &BR);
+ static PathDiagnosticPieceRef
+ getDefaultEndPath(const BugReporterContext &BRC, const ExplodedNode *N,
+ const PathSensitiveBugReport &BR);
};
+namespace bugreporter {
+
+/// Specifies the type of tracking for an expression.
+enum class TrackingKind {
+ /// Default tracking kind -- specifies that as much information should be
+ /// gathered about the tracked expression value as possible.
+ Thorough,
+ /// Specifies that a more moderate tracking should be used for the expression
+ /// value. This will essentially make sure that functions relevant to the it
+ /// aren't pruned, but otherwise relies on the user reading the code or
+ /// following the arrows.
+ Condition
+};
+
+/// Attempts to add visitors to track expression value back to its point of
+/// origin.
+///
+/// \param N A node "downstream" from the evaluation of the statement.
+/// \param E The expression value which we are tracking
+/// \param R The bug report to which visitors should be attached.
+/// \param EnableNullFPSuppression Whether we should employ false positive
+/// suppression (inlined defensive checks, returned null).
+///
+/// \return Whether or not the function was able to add visitors for this
+/// statement. Note that returning \c true does not actually imply
+/// that any visitors were added.
+bool trackExpressionValue(const ExplodedNode *N, const Expr *E,
+ PathSensitiveBugReport &R,
+ TrackingKind TKind = TrackingKind::Thorough,
+ bool EnableNullFPSuppression = true);
+
+const Expr *getDerefExpr(const Stmt *S);
+
+} // namespace bugreporter
+
/// Finds last store into the given region,
/// which is different from a given symbolic value.
class FindLastStoreBRVisitor final : public BugReporterVisitor {
@@ -94,21 +131,34 @@ class FindLastStoreBRVisitor final : public BugReporterVisitor {
/// bug, we are going to employ false positive suppression.
bool EnableNullFPSuppression;
-public:
- /// Creates a visitor for every VarDecl inside a Stmt and registers it with
- /// the BugReport.
- static void registerStatementVarDecls(BugReport &BR, const Stmt *S,
- bool EnableNullFPSuppression);
+ using TrackingKind = bugreporter::TrackingKind;
+ TrackingKind TKind;
+ const StackFrameContext *OriginSFC;
+public:
+ /// \param V We're searching for the store where \c R received this value.
+ /// \param R The region we're tracking.
+ /// \param TKind May limit the amount of notes added to the bug report.
+ /// \param OriginSFC Only adds notes when the last store happened in a
+ /// different stackframe to this one. Disregarded if the tracking kind
+ /// is thorough.
+ /// This is useful, because for non-tracked regions, notes about
+ /// changes to its value in a nested stackframe could be pruned, and
+ /// this visitor can prevent that without polluting the bugpath too
+ /// much.
FindLastStoreBRVisitor(KnownSVal V, const MemRegion *R,
- bool InEnableNullFPSuppression)
- : R(R), V(V), EnableNullFPSuppression(InEnableNullFPSuppression) {}
+ bool InEnableNullFPSuppression, TrackingKind TKind,
+ const StackFrameContext *OriginSFC = nullptr)
+ : R(R), V(V), EnableNullFPSuppression(InEnableNullFPSuppression),
+ TKind(TKind), OriginSFC(OriginSFC) {
+ assert(R);
+ }
void Profile(llvm::FoldingSetNodeID &ID) const override;
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
};
class TrackConstraintBRVisitor final : public BugReporterVisitor {
@@ -132,9 +182,9 @@ public:
/// to make all PathDiagnosticPieces created by this visitor.
static const char *getTag();
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
private:
/// Checks if the constraint is valid in the current state.
@@ -150,9 +200,9 @@ public:
ID.AddPointer(&x);
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
/// If the statement is a message send expression with nil receiver, returns
/// the receiver expression. Returns NULL otherwise.
@@ -162,8 +212,10 @@ public:
/// Visitor that tries to report interesting diagnostics from conditions.
class ConditionBRVisitor final : public BugReporterVisitor {
// FIXME: constexpr initialization isn't supported by MSVC2013.
- static const char *const GenericTrueMessage;
- static const char *const GenericFalseMessage;
+ constexpr static llvm::StringLiteral GenericTrueMessage =
+ "Assuming the condition is true";
+ constexpr static llvm::StringLiteral GenericFalseMessage =
+ "Assuming the condition is false";
public:
void Profile(llvm::FoldingSetNodeID &ID) const override {
@@ -175,41 +227,44 @@ public:
/// to make all PathDiagnosticPieces created by this visitor.
static const char *getTag();
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
- std::shared_ptr<PathDiagnosticPiece> VisitNodeImpl(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR);
+ PathDiagnosticPieceRef VisitNodeImpl(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR);
- std::shared_ptr<PathDiagnosticPiece>
+ PathDiagnosticPieceRef
VisitTerminator(const Stmt *Term, const ExplodedNode *N,
- const CFGBlock *srcBlk, const CFGBlock *dstBlk, BugReport &R,
- BugReporterContext &BRC);
+ const CFGBlock *SrcBlk, const CFGBlock *DstBlk,
+ PathSensitiveBugReport &R, BugReporterContext &BRC);
- std::shared_ptr<PathDiagnosticPiece>
- VisitTrueTest(const Expr *Cond, BugReporterContext &BRC, BugReport &R,
- const ExplodedNode *N, bool TookTrue);
+ PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &R,
+ const ExplodedNode *N, bool TookTrue);
- std::shared_ptr<PathDiagnosticPiece>
- VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR,
- BugReporterContext &BRC, BugReport &R, const ExplodedNode *N,
- bool TookTrue, bool IsAssuming);
+ PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &R,
+ const ExplodedNode *N, bool TookTrue,
+ bool IsAssuming);
- std::shared_ptr<PathDiagnosticPiece>
+ PathDiagnosticPieceRef
VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr,
- BugReporterContext &BRC, BugReport &R, const ExplodedNode *N,
- bool TookTrue, bool IsAssuming);
+ BugReporterContext &BRC, PathSensitiveBugReport &R,
+ const ExplodedNode *N, bool TookTrue, bool IsAssuming);
- std::shared_ptr<PathDiagnosticPiece>
- VisitTrueTest(const Expr *Cond, const MemberExpr *ME, BugReporterContext &BRC,
- BugReport &R, const ExplodedNode *N, bool TookTrue,
- bool IsAssuming);
+ PathDiagnosticPieceRef VisitTrueTest(const Expr *Cond, const MemberExpr *ME,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &R,
+ const ExplodedNode *N, bool TookTrue,
+ bool IsAssuming);
- std::shared_ptr<PathDiagnosticPiece>
+ PathDiagnosticPieceRef
VisitConditionVariable(StringRef LhsString, const Expr *CondVarExpr,
- BugReporterContext &BRC, BugReport &R,
+ BugReporterContext &BRC, PathSensitiveBugReport &R,
const ExplodedNode *N, bool TookTrue);
/// Tries to print the value of the given expression.
@@ -228,7 +283,7 @@ public:
const Expr *ParentEx,
raw_ostream &Out,
BugReporterContext &BRC,
- BugReport &R,
+ PathSensitiveBugReport &R,
const ExplodedNode *N,
Optional<bool> &prunable,
bool IsSameFieldName);
@@ -251,14 +306,13 @@ public:
ID.AddPointer(getTag());
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *,
- BugReporterContext &,
- BugReport &) override {
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *, BugReporterContext &,
+ PathSensitiveBugReport &) override {
return nullptr;
}
void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *N,
- BugReport &BR) override;
+ PathSensitiveBugReport &BR) override;
};
/// When a region containing undefined value or '0' value is passed
@@ -279,9 +333,9 @@ public:
ID.AddPointer(R);
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
};
class SuppressInlineDefensiveChecksVisitor final : public BugReporterVisitor {
@@ -308,9 +362,9 @@ public:
/// to make all PathDiagnosticPieces created by this visitor.
static const char *getTag();
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
};
/// The bug visitor will walk all the nodes in a path and collect all the
@@ -326,12 +380,12 @@ public:
void Profile(llvm::FoldingSetNodeID &ID) const override;
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *EndPathNode,
- BugReport &BR) override;
+ PathSensitiveBugReport &BR) override;
};
@@ -340,32 +394,11 @@ class TagVisitor : public BugReporterVisitor {
public:
void Profile(llvm::FoldingSetNodeID &ID) const override;
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &R) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &R) override;
};
-namespace bugreporter {
-
-/// Attempts to add visitors to track expression value back to its point of
-/// origin.
-///
-/// \param N A node "downstream" from the evaluation of the statement.
-/// \param E The expression value which we are tracking
-/// \param R The bug report to which visitors should be attached.
-/// \param EnableNullFPSuppression Whether we should employ false positive
-/// suppression (inlined defensive checks, returned null).
-///
-/// \return Whether or not the function was able to add visitors for this
-/// statement. Note that returning \c true does not actually imply
-/// that any visitors were added.
-bool trackExpressionValue(const ExplodedNode *N, const Expr *E, BugReport &R,
- bool EnableNullFPSuppression = true);
-
-const Expr *getDerefExpr(const Stmt *S);
-
-} // namespace bugreporter
-
} // namespace ento
} // namespace clang
diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h
index 324b5312e790..237053df7e44 100644
--- a/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h
+++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugType.h
@@ -28,8 +28,8 @@ class ExprEngine;
class BugType {
private:
- const CheckName Check;
- const std::string Name;
+ const CheckerNameRef CheckerName;
+ const std::string Description;
const std::string Category;
const CheckerBase *Checker;
bool SuppressOnSink;
@@ -37,28 +37,27 @@ private:
virtual void anchor();
public:
- BugType(CheckName Check, StringRef Name, StringRef Cat,
- bool SuppressOnSink=false)
- : Check(Check), Name(Name), Category(Cat), Checker(nullptr),
- SuppressOnSink(SuppressOnSink) {}
+ BugType(CheckerNameRef CheckerName, StringRef Name, StringRef Cat,
+ bool SuppressOnSink = false)
+ : CheckerName(CheckerName), Description(Name), Category(Cat),
+ Checker(nullptr), SuppressOnSink(SuppressOnSink) {}
BugType(const CheckerBase *Checker, StringRef Name, StringRef Cat,
- bool SuppressOnSink=false)
- : Check(Checker->getCheckName()), Name(Name), Category(Cat),
- Checker(Checker), SuppressOnSink(SuppressOnSink) {}
+ bool SuppressOnSink = false)
+ : CheckerName(Checker->getCheckerName()), Description(Name),
+ Category(Cat), Checker(Checker), SuppressOnSink(SuppressOnSink) {}
virtual ~BugType() = default;
- StringRef getName() const { return Name; }
+ StringRef getDescription() const { return Description; }
StringRef getCategory() const { return Category; }
- StringRef getCheckName() const {
- // FIXME: This is a workaround to ensure that the correct check name is used
- // The check names are set after the constructors are run.
+ StringRef getCheckerName() const {
+ // FIXME: This is a workaround to ensure that the correct checerk name is
+ // used. The checker names are set after the constructors are run.
// In case the BugType object is initialized in the checker's ctor
- // the Check field will be empty. To circumvent this problem we use
+ // the CheckerName field will be empty. To circumvent this problem we use
// CheckerBase whenever it is possible.
- StringRef CheckName =
- Checker ? Checker->getCheckName().getName() : Check.getName();
- assert(!CheckName.empty() && "Check name is not set properly.");
- return CheckName;
+ StringRef Ret = Checker ? Checker->getCheckerName() : CheckerName;
+ assert(!Ret.empty() && "Checker name is not set properly.");
+ return Ret;
}
/// isSuppressOnSink - Returns true if bug reports associated with this bug
@@ -71,8 +70,9 @@ class BuiltinBug : public BugType {
const std::string desc;
void anchor() override;
public:
- BuiltinBug(class CheckName check, const char *name, const char *description)
- : BugType(check, name, categories::LogicError), desc(description) {}
+ BuiltinBug(class CheckerNameRef checker, const char *name,
+ const char *description)
+ : BugType(checker, name, categories::LogicError), desc(description) {}
BuiltinBug(const CheckerBase *checker, const char *name,
const char *description)
diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h b/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h
index 85526eb49f0c..22c1a7dd98cc 100644
--- a/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h
+++ b/include/clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h
@@ -18,6 +18,7 @@ namespace clang {
extern const char * const MemoryRefCount;
extern const char * const MemoryError;
extern const char * const UnixAPI;
+ extern const char * const CXXObjectLifecycle;
}
}
}
diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
deleted file mode 100644
index 5230742a4aa4..000000000000
--- a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h
+++ /dev/null
@@ -1,923 +0,0 @@
-//===- PathDiagnostic.h - Path-Specific Diagnostic Handling -----*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines the PathDiagnostic-related interfaces.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H
-#define LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H
-
-#include "clang/AST/Stmt.h"
-#include "clang/Analysis/AnalysisDeclContext.h"
-#include "clang/Basic/LLVM.h"
-#include "clang/Basic/SourceLocation.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/Optional.h"
-#include "llvm/ADT/PointerUnion.h"
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Allocator.h"
-#include <cassert>
-#include <deque>
-#include <iterator>
-#include <list>
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-namespace clang {
-
-class AnalysisDeclContext;
-class BinaryOperator;
-class CallEnter;
-class CallExitEnd;
-class CallExpr;
-class ConditionalOperator;
-class Decl;
-class Expr;
-class LocationContext;
-class MemberExpr;
-class ProgramPoint;
-class SourceManager;
-
-namespace ento {
-
-class ExplodedNode;
-class SymExpr;
-
-using SymbolRef = const SymExpr *;
-
-//===----------------------------------------------------------------------===//
-// High-level interface for handlers of path-sensitive diagnostics.
-//===----------------------------------------------------------------------===//
-
-class PathDiagnostic;
-
-class PathDiagnosticConsumer {
-public:
- class PDFileEntry : public llvm::FoldingSetNode {
- public:
- PDFileEntry(llvm::FoldingSetNodeID &NodeID) : NodeID(NodeID) {}
-
- using ConsumerFiles = std::vector<std::pair<StringRef, StringRef>>;
-
- /// A vector of <consumer,file> pairs.
- ConsumerFiles files;
-
- /// A precomputed hash tag used for uniquing PDFileEntry objects.
- const llvm::FoldingSetNodeID NodeID;
-
- /// Used for profiling in the FoldingSet.
- void Profile(llvm::FoldingSetNodeID &ID) { ID = NodeID; }
- };
-
- class FilesMade {
- llvm::BumpPtrAllocator Alloc;
- llvm::FoldingSet<PDFileEntry> Set;
-
- public:
- ~FilesMade();
-
- bool empty() const { return Set.empty(); }
-
- void addDiagnostic(const PathDiagnostic &PD,
- StringRef ConsumerName,
- StringRef fileName);
-
- PDFileEntry::ConsumerFiles *getFiles(const PathDiagnostic &PD);
- };
-
-private:
- virtual void anchor();
-
-public:
- PathDiagnosticConsumer() = default;
- virtual ~PathDiagnosticConsumer();
-
- void FlushDiagnostics(FilesMade *FilesMade);
-
- virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
- FilesMade *filesMade) = 0;
-
- virtual StringRef getName() const = 0;
-
- void HandlePathDiagnostic(std::unique_ptr<PathDiagnostic> D);
-
- enum PathGenerationScheme {
- /// Only runs visitors, no output generated.
- None,
-
- /// Used for HTML, SARIF, and text output.
- Minimal,
-
- /// Used for plist output, used for "arrows" generation.
- Extensive,
- };
-
- virtual PathGenerationScheme getGenerationScheme() const { return Minimal; }
- virtual bool supportsLogicalOpControlFlow() const { return false; }
-
- /// Return true if the PathDiagnosticConsumer supports individual
- /// PathDiagnostics that span multiple files.
- virtual bool supportsCrossFileDiagnostics() const { return false; }
-
-protected:
- bool flushed = false;
- llvm::FoldingSet<PathDiagnostic> Diags;
-};
-
-//===----------------------------------------------------------------------===//
-// Path-sensitive diagnostics.
-//===----------------------------------------------------------------------===//
-
-class PathDiagnosticRange : public SourceRange {
-public:
- bool isPoint = false;
-
- PathDiagnosticRange(SourceRange R, bool isP = false)
- : SourceRange(R), isPoint(isP) {}
- PathDiagnosticRange() = default;
-};
-
-using LocationOrAnalysisDeclContext =
- llvm::PointerUnion<const LocationContext *, AnalysisDeclContext *>;
-
-class PathDiagnosticLocation {
-private:
- enum Kind { RangeK, SingleLocK, StmtK, DeclK } K = SingleLocK;
-
- const Stmt *S = nullptr;
- const Decl *D = nullptr;
- const SourceManager *SM = nullptr;
- FullSourceLoc Loc;
- PathDiagnosticRange Range;
-
- PathDiagnosticLocation(SourceLocation L, const SourceManager &sm, Kind kind)
- : K(kind), SM(&sm), Loc(genLocation(L)), Range(genRange()) {}
-
- FullSourceLoc genLocation(
- SourceLocation L = SourceLocation(),
- LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const;
-
- PathDiagnosticRange genRange(
- LocationOrAnalysisDeclContext LAC = (AnalysisDeclContext *)nullptr) const;
-
-public:
- /// Create an invalid location.
- PathDiagnosticLocation() = default;
-
- /// Create a location corresponding to the given statement.
- PathDiagnosticLocation(const Stmt *s, const SourceManager &sm,
- LocationOrAnalysisDeclContext lac)
- : K(s->getBeginLoc().isValid() ? StmtK : SingleLocK),
- S(K == StmtK ? s : nullptr), SM(&sm),
- Loc(genLocation(SourceLocation(), lac)), Range(genRange(lac)) {
- assert(K == SingleLocK || S);
- assert(K == SingleLocK || Loc.isValid());
- assert(K == SingleLocK || Range.isValid());
- }
-
- /// Create a location corresponding to the given declaration.
- PathDiagnosticLocation(const Decl *d, const SourceManager &sm)
- : K(DeclK), D(d), SM(&sm), Loc(genLocation()), Range(genRange()) {
- assert(D);
- assert(Loc.isValid());
- assert(Range.isValid());
- }
-
- /// Create a location at an explicit offset in the source.
- ///
- /// This should only be used if there are no more appropriate constructors.
- PathDiagnosticLocation(SourceLocation loc, const SourceManager &sm)
- : SM(&sm), Loc(loc, sm), Range(genRange()) {
- assert(Loc.isValid());
- assert(Range.isValid());
- }
-
- /// Create a location corresponding to the given declaration.
- static PathDiagnosticLocation create(const Decl *D,
- const SourceManager &SM) {
- return PathDiagnosticLocation(D, SM);
- }
-
- /// Create a location for the beginning of the declaration.
- static PathDiagnosticLocation createBegin(const Decl *D,
- const SourceManager &SM);
-
- /// Create a location for the beginning of the declaration.
- /// The third argument is ignored, useful for generic treatment
- /// of statements and declarations.
- static PathDiagnosticLocation
- createBegin(const Decl *D, const SourceManager &SM,
- const LocationOrAnalysisDeclContext LAC) {
- return createBegin(D, SM);
- }
-
- /// Create a location for the beginning of the statement.
- static PathDiagnosticLocation createBegin(const Stmt *S,
- const SourceManager &SM,
- const LocationOrAnalysisDeclContext LAC);
-
- /// Create a location for the end of the statement.
- ///
- /// If the statement is a CompoundStatement, the location will point to the
- /// closing brace instead of following it.
- static PathDiagnosticLocation createEnd(const Stmt *S,
- const SourceManager &SM,
- const LocationOrAnalysisDeclContext LAC);
-
- /// Create the location for the operator of the binary expression.
- /// Assumes the statement has a valid location.
- static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO,
- const SourceManager &SM);
- static PathDiagnosticLocation createConditionalColonLoc(
- const ConditionalOperator *CO,
- const SourceManager &SM);
-
- /// For member expressions, return the location of the '.' or '->'.
- /// Assumes the statement has a valid location.
- static PathDiagnosticLocation createMemberLoc(const MemberExpr *ME,
- const SourceManager &SM);
-
- /// Create a location for the beginning of the compound statement.
- /// Assumes the statement has a valid location.
- static PathDiagnosticLocation createBeginBrace(const CompoundStmt *CS,
- const SourceManager &SM);
-
- /// Create a location for the end of the compound statement.
- /// Assumes the statement has a valid location.
- static PathDiagnosticLocation createEndBrace(const CompoundStmt *CS,
- const SourceManager &SM);
-
- /// Create a location for the beginning of the enclosing declaration body.
- /// Defaults to the beginning of the first statement in the declaration body.
- static PathDiagnosticLocation createDeclBegin(const LocationContext *LC,
- const SourceManager &SM);
-
- /// Constructs a location for the end of the enclosing declaration body.
- /// Defaults to the end of brace.
- static PathDiagnosticLocation createDeclEnd(const LocationContext *LC,
- const SourceManager &SM);
-
- /// Create a location corresponding to the given valid ExplodedNode.
- static PathDiagnosticLocation create(const ProgramPoint &P,
- const SourceManager &SMng);
-
- /// Create a location corresponding to the next valid ExplodedNode as end
- /// of path location.
- static PathDiagnosticLocation createEndOfPath(const ExplodedNode* N,
- const SourceManager &SM);
-
- /// Convert the given location into a single kind location.
- static PathDiagnosticLocation createSingleLocation(
- const PathDiagnosticLocation &PDL);
-
- bool operator==(const PathDiagnosticLocation &X) const {
- return K == X.K && Loc == X.Loc && Range == X.Range;
- }
-
- bool operator!=(const PathDiagnosticLocation &X) const {
- return !(*this == X);
- }
-
- bool isValid() const {
- return SM != nullptr;
- }
-
- FullSourceLoc asLocation() const {
- return Loc;
- }
-
- PathDiagnosticRange asRange() const {
- return Range;
- }
-
- const Stmt *asStmt() const { assert(isValid()); return S; }
- const Stmt *getStmtOrNull() const {
- if (!isValid())
- return nullptr;
- return asStmt();
- }
-
- const Decl *asDecl() const { assert(isValid()); return D; }
-
- bool hasRange() const { return K == StmtK || K == RangeK || K == DeclK; }
-
- bool hasValidLocation() const { return asLocation().isValid(); }
-
- void invalidate() {
- *this = PathDiagnosticLocation();
- }
-
- void flatten();
-
- const SourceManager& getManager() const { assert(isValid()); return *SM; }
-
- void Profile(llvm::FoldingSetNodeID &ID) const;
-
- void dump() const;
-
- /// Given an exploded node, retrieve the statement that should be used
- /// for the diagnostic location.
- static const Stmt *getStmt(const ExplodedNode *N);
-
- /// Retrieve the statement corresponding to the successor node.
- static const Stmt *getNextStmt(const ExplodedNode *N);
-};
-
-class PathDiagnosticLocationPair {
-private:
- PathDiagnosticLocation Start, End;
-
-public:
- PathDiagnosticLocationPair(const PathDiagnosticLocation &start,
- const PathDiagnosticLocation &end)
- : Start(start), End(end) {}
-
- const PathDiagnosticLocation &getStart() const { return Start; }
- const PathDiagnosticLocation &getEnd() const { return End; }
-
- void setStart(const PathDiagnosticLocation &L) { Start = L; }
- void setEnd(const PathDiagnosticLocation &L) { End = L; }
-
- void flatten() {
- Start.flatten();
- End.flatten();
- }
-
- void Profile(llvm::FoldingSetNodeID &ID) const {
- Start.Profile(ID);
- End.Profile(ID);
- }
-};
-
-//===----------------------------------------------------------------------===//
-// Path "pieces" for path-sensitive diagnostics.
-//===----------------------------------------------------------------------===//
-
-class PathDiagnosticPiece: public llvm::FoldingSetNode {
-public:
- enum Kind { ControlFlow, Event, Macro, Call, Note, PopUp };
- enum DisplayHint { Above, Below };
-
-private:
- const std::string str;
- const Kind kind;
- const DisplayHint Hint;
-
- /// In the containing bug report, this piece is the last piece from
- /// the main source file.
- bool LastInMainSourceFile = false;
-
- /// A constant string that can be used to tag the PathDiagnosticPiece,
- /// typically with the identification of the creator. The actual pointer
- /// value is meant to be an identifier; the string itself is useful for
- /// debugging.
- StringRef Tag;
-
- std::vector<SourceRange> ranges;
-
-protected:
- PathDiagnosticPiece(StringRef s, Kind k, DisplayHint hint = Below);
- PathDiagnosticPiece(Kind k, DisplayHint hint = Below);
-
-public:
- PathDiagnosticPiece() = delete;
- PathDiagnosticPiece(const PathDiagnosticPiece &) = delete;
- PathDiagnosticPiece &operator=(const PathDiagnosticPiece &) = delete;
- virtual ~PathDiagnosticPiece();
-
- StringRef getString() const { return str; }
-
- /// Tag this PathDiagnosticPiece with the given C-string.
- void setTag(const char *tag) { Tag = tag; }
-
- /// Return the opaque tag (if any) on the PathDiagnosticPiece.
- const void *getTag() const { return Tag.data(); }
-
- /// Return the string representation of the tag. This is useful
- /// for debugging.
- StringRef getTagStr() const { return Tag; }
-
- /// getDisplayHint - Return a hint indicating where the diagnostic should
- /// be displayed by the PathDiagnosticConsumer.
- DisplayHint getDisplayHint() const { return Hint; }
-
- virtual PathDiagnosticLocation getLocation() const = 0;
- virtual void flattenLocations() = 0;
-
- Kind getKind() const { return kind; }
-
- void addRange(SourceRange R) {
- if (!R.isValid())
- return;
- ranges.push_back(R);
- }
-
- void addRange(SourceLocation B, SourceLocation E) {
- if (!B.isValid() || !E.isValid())
- return;
- ranges.push_back(SourceRange(B,E));
- }
-
- /// Return the SourceRanges associated with this PathDiagnosticPiece.
- ArrayRef<SourceRange> getRanges() const { return ranges; }
-
- virtual void Profile(llvm::FoldingSetNodeID &ID) const;
-
- void setAsLastInMainSourceFile() {
- LastInMainSourceFile = true;
- }
-
- bool isLastInMainSourceFile() const {
- return LastInMainSourceFile;
- }
-
- virtual void dump() const = 0;
-};
-
-class PathPieces : public std::list<std::shared_ptr<PathDiagnosticPiece>> {
- void flattenTo(PathPieces &Primary, PathPieces &Current,
- bool ShouldFlattenMacros) const;
-
-public:
- PathPieces flatten(bool ShouldFlattenMacros) const {
- PathPieces Result;
- flattenTo(Result, Result, ShouldFlattenMacros);
- return Result;
- }
-
- void dump() const;
-};
-
-class PathDiagnosticSpotPiece : public PathDiagnosticPiece {
-private:
- PathDiagnosticLocation Pos;
-
-public:
- PathDiagnosticSpotPiece(const PathDiagnosticLocation &pos,
- StringRef s,
- PathDiagnosticPiece::Kind k,
- bool addPosRange = true)
- : PathDiagnosticPiece(s, k), Pos(pos) {
- assert(Pos.isValid() && Pos.hasValidLocation() &&
- "PathDiagnosticSpotPiece's must have a valid location.");
- if (addPosRange && Pos.hasRange()) addRange(Pos.asRange());
- }
-
- PathDiagnosticLocation getLocation() const override { return Pos; }
- void flattenLocations() override { Pos.flatten(); }
-
- void Profile(llvm::FoldingSetNodeID &ID) const override;
-
- static bool classof(const PathDiagnosticPiece *P) {
- return P->getKind() == Event || P->getKind() == Macro ||
- P->getKind() == Note || P->getKind() == PopUp;
- }
-};
-
-/// Interface for classes constructing Stack hints.
-///
-/// If a PathDiagnosticEvent occurs in a different frame than the final
-/// diagnostic the hints can be used to summarize the effect of the call.
-class StackHintGenerator {
-public:
- virtual ~StackHintGenerator() = 0;
-
- /// Construct the Diagnostic message for the given ExplodedNode.
- virtual std::string getMessage(const ExplodedNode *N) = 0;
-};
-
-/// Constructs a Stack hint for the given symbol.
-///
-/// The class knows how to construct the stack hint message based on
-/// traversing the CallExpr associated with the call and checking if the given
-/// symbol is returned or is one of the arguments.
-/// The hint can be customized by redefining 'getMessageForX()' methods.
-class StackHintGeneratorForSymbol : public StackHintGenerator {
-private:
- SymbolRef Sym;
- std::string Msg;
-
-public:
- StackHintGeneratorForSymbol(SymbolRef S, StringRef M) : Sym(S), Msg(M) {}
- ~StackHintGeneratorForSymbol() override = default;
-
- /// Search the call expression for the symbol Sym and dispatch the
- /// 'getMessageForX()' methods to construct a specific message.
- std::string getMessage(const ExplodedNode *N) override;
-
- /// Produces the message of the following form:
- /// 'Msg via Nth parameter'
- virtual std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex);
-
- virtual std::string getMessageForReturn(const CallExpr *CallExpr) {
- return Msg;
- }
-
- virtual std::string getMessageForSymbolNotFound() {
- return Msg;
- }
-};
-
-class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece {
- Optional<bool> IsPrunable;
-
- /// If the event occurs in a different frame than the final diagnostic,
- /// supply a message that will be used to construct an extra hint on the
- /// returns from all the calls on the stack from this event to the final
- /// diagnostic.
- std::unique_ptr<StackHintGenerator> CallStackHint;
-
-public:
- PathDiagnosticEventPiece(const PathDiagnosticLocation &pos,
- StringRef s, bool addPosRange = true,
- StackHintGenerator *stackHint = nullptr)
- : PathDiagnosticSpotPiece(pos, s, Event, addPosRange),
- CallStackHint(stackHint) {}
- ~PathDiagnosticEventPiece() override;
-
- /// Mark the diagnostic piece as being potentially prunable. This
- /// flag may have been previously set, at which point it will not
- /// be reset unless one specifies to do so.
- void setPrunable(bool isPrunable, bool override = false) {
- if (IsPrunable.hasValue() && !override)
- return;
- IsPrunable = isPrunable;
- }
-
- /// Return true if the diagnostic piece is prunable.
- bool isPrunable() const {
- return IsPrunable.hasValue() ? IsPrunable.getValue() : false;
- }
-
- bool hasCallStackHint() { return (bool)CallStackHint; }
-
- /// Produce the hint for the given node. The node contains
- /// information about the call for which the diagnostic can be generated.
- std::string getCallStackMessage(const ExplodedNode *N) {
- if (CallStackHint)
- return CallStackHint->getMessage(N);
- return {};
- }
-
- void dump() const override;
-
- static bool classof(const PathDiagnosticPiece *P) {
- return P->getKind() == Event;
- }
-};
-
-class PathDiagnosticCallPiece : public PathDiagnosticPiece {
- const Decl *Caller;
- const Decl *Callee = nullptr;
-
- // Flag signifying that this diagnostic has only call enter and no matching
- // call exit.
- bool NoExit;
-
- // Flag signifying that the callee function is an Objective-C autosynthesized
- // property getter or setter.
- bool IsCalleeAnAutosynthesizedPropertyAccessor = false;
-
- // The custom string, which should appear after the call Return Diagnostic.
- // TODO: Should we allow multiple diagnostics?
- std::string CallStackMessage;
-
- PathDiagnosticCallPiece(const Decl *callerD,
- const PathDiagnosticLocation &callReturnPos)
- : PathDiagnosticPiece(Call), Caller(callerD), NoExit(false),
- callReturn(callReturnPos) {}
- PathDiagnosticCallPiece(PathPieces &oldPath, const Decl *caller)
- : PathDiagnosticPiece(Call), Caller(caller), NoExit(true),
- path(oldPath) {}
-
-public:
- PathDiagnosticLocation callEnter;
- PathDiagnosticLocation callEnterWithin;
- PathDiagnosticLocation callReturn;
- PathPieces path;
-
- ~PathDiagnosticCallPiece() override;
-
- const Decl *getCaller() const { return Caller; }
-
- const Decl *getCallee() const { return Callee; }
- void setCallee(const CallEnter &CE, const SourceManager &SM);
-
- bool hasCallStackMessage() { return !CallStackMessage.empty(); }
- void setCallStackMessage(StringRef st) { CallStackMessage = st; }
-
- PathDiagnosticLocation getLocation() const override { return callEnter; }
-
- std::shared_ptr<PathDiagnosticEventPiece> getCallEnterEvent() const;
- std::shared_ptr<PathDiagnosticEventPiece>
- getCallEnterWithinCallerEvent() const;
- std::shared_ptr<PathDiagnosticEventPiece> getCallExitEvent() const;
-
- void flattenLocations() override {
- callEnter.flatten();
- callReturn.flatten();
- for (const auto &I : path)
- I->flattenLocations();
- }
-
- static std::shared_ptr<PathDiagnosticCallPiece>
- construct(const CallExitEnd &CE,
- const SourceManager &SM);
-
- static PathDiagnosticCallPiece *construct(PathPieces &pieces,
- const Decl *caller);
-
- void dump() const override;
-
- void Profile(llvm::FoldingSetNodeID &ID) const override;
-
- static bool classof(const PathDiagnosticPiece *P) {
- return P->getKind() == Call;
- }
-};
-
-class PathDiagnosticControlFlowPiece : public PathDiagnosticPiece {
- std::vector<PathDiagnosticLocationPair> LPairs;
-
-public:
- PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos,
- const PathDiagnosticLocation &endPos,
- StringRef s)
- : PathDiagnosticPiece(s, ControlFlow) {
- LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos));
- }
-
- PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos,
- const PathDiagnosticLocation &endPos)
- : PathDiagnosticPiece(ControlFlow) {
- LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos));
- }
-
- ~PathDiagnosticControlFlowPiece() override;
-
- PathDiagnosticLocation getStartLocation() const {
- assert(!LPairs.empty() &&
- "PathDiagnosticControlFlowPiece needs at least one location.");
- return LPairs[0].getStart();
- }
-
- PathDiagnosticLocation getEndLocation() const {
- assert(!LPairs.empty() &&
- "PathDiagnosticControlFlowPiece needs at least one location.");
- return LPairs[0].getEnd();
- }
-
- void setStartLocation(const PathDiagnosticLocation &L) {
- LPairs[0].setStart(L);
- }
-
- void setEndLocation(const PathDiagnosticLocation &L) {
- LPairs[0].setEnd(L);
- }
-
- void push_back(const PathDiagnosticLocationPair &X) { LPairs.push_back(X); }
-
- PathDiagnosticLocation getLocation() const override {
- return getStartLocation();
- }
-
- using iterator = std::vector<PathDiagnosticLocationPair>::iterator;
-
- iterator begin() { return LPairs.begin(); }
- iterator end() { return LPairs.end(); }
-
- void flattenLocations() override {
- for (auto &I : *this)
- I.flatten();
- }
-
- using const_iterator =
- std::vector<PathDiagnosticLocationPair>::const_iterator;
-
- const_iterator begin() const { return LPairs.begin(); }
- const_iterator end() const { return LPairs.end(); }
-
- static bool classof(const PathDiagnosticPiece *P) {
- return P->getKind() == ControlFlow;
- }
-
- void dump() const override;
-
- void Profile(llvm::FoldingSetNodeID &ID) const override;
-};
-
-class PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece {
-public:
- PathDiagnosticMacroPiece(const PathDiagnosticLocation &pos)
- : PathDiagnosticSpotPiece(pos, "", Macro) {}
- ~PathDiagnosticMacroPiece() override;
-
- PathPieces subPieces;
-
- bool containsEvent() const;
-
- void flattenLocations() override {
- PathDiagnosticSpotPiece::flattenLocations();
- for (const auto &I : subPieces)
- I->flattenLocations();
- }
-
- static bool classof(const PathDiagnosticPiece *P) {
- return P->getKind() == Macro;
- }
-
- void dump() const override;
-
- void Profile(llvm::FoldingSetNodeID &ID) const override;
-};
-
-class PathDiagnosticNotePiece: public PathDiagnosticSpotPiece {
-public:
- PathDiagnosticNotePiece(const PathDiagnosticLocation &Pos, StringRef S,
- bool AddPosRange = true)
- : PathDiagnosticSpotPiece(Pos, S, Note, AddPosRange) {}
- ~PathDiagnosticNotePiece() override;
-
- static bool classof(const PathDiagnosticPiece *P) {
- return P->getKind() == Note;
- }
-
- void dump() const override;
-
- void Profile(llvm::FoldingSetNodeID &ID) const override;
-};
-
-class PathDiagnosticPopUpPiece: public PathDiagnosticSpotPiece {
-public:
- PathDiagnosticPopUpPiece(const PathDiagnosticLocation &Pos, StringRef S,
- bool AddPosRange = true)
- : PathDiagnosticSpotPiece(Pos, S, PopUp, AddPosRange) {}
- ~PathDiagnosticPopUpPiece() override;
-
- static bool classof(const PathDiagnosticPiece *P) {
- return P->getKind() == PopUp;
- }
-
- void dump() const override;
-
- void Profile(llvm::FoldingSetNodeID &ID) const override;
-};
-
-/// File IDs mapped to sets of line numbers.
-using FilesToLineNumsMap = std::map<FileID, std::set<unsigned>>;
-
-/// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive
-/// diagnostic. It represents an ordered-collection of PathDiagnosticPieces,
-/// each which represent the pieces of the path.
-class PathDiagnostic : public llvm::FoldingSetNode {
- std::string CheckName;
- const Decl *DeclWithIssue;
- std::string BugType;
- std::string VerboseDesc;
- std::string ShortDesc;
- std::string Category;
- std::deque<std::string> OtherDesc;
-
- /// Loc The location of the path diagnostic report.
- PathDiagnosticLocation Loc;
-
- PathPieces pathImpl;
- SmallVector<PathPieces *, 3> pathStack;
-
- /// Important bug uniqueing location.
- /// The location info is useful to differentiate between bugs.
- PathDiagnosticLocation UniqueingLoc;
- const Decl *UniqueingDecl;
-
- /// Lines executed in the path.
- std::unique_ptr<FilesToLineNumsMap> ExecutedLines;
-
-public:
- PathDiagnostic() = delete;
- PathDiagnostic(StringRef CheckName, const Decl *DeclWithIssue,
- StringRef bugtype, StringRef verboseDesc, StringRef shortDesc,
- StringRef category, PathDiagnosticLocation LocationToUnique,
- const Decl *DeclToUnique,
- std::unique_ptr<FilesToLineNumsMap> ExecutedLines);
- ~PathDiagnostic();
-
- const PathPieces &path;
-
- /// Return the path currently used by builders for constructing the
- /// PathDiagnostic.
- PathPieces &getActivePath() {
- if (pathStack.empty())
- return pathImpl;
- return *pathStack.back();
- }
-
- /// Return a mutable version of 'path'.
- PathPieces &getMutablePieces() {
- return pathImpl;
- }
-
- /// Return the unrolled size of the path.
- unsigned full_size();
-
- void pushActivePath(PathPieces *p) { pathStack.push_back(p); }
- void popActivePath() { if (!pathStack.empty()) pathStack.pop_back(); }
-
- bool isWithinCall() const { return !pathStack.empty(); }
-
- void setEndOfPath(std::shared_ptr<PathDiagnosticPiece> EndPiece) {
- assert(!Loc.isValid() && "End location already set!");
- Loc = EndPiece->getLocation();
- assert(Loc.isValid() && "Invalid location for end-of-path piece");
- getActivePath().push_back(std::move(EndPiece));
- }
-
- void appendToDesc(StringRef S) {
- if (!ShortDesc.empty())
- ShortDesc += S;
- VerboseDesc += S;
- }
-
- /// If the last piece of the report point to the header file, resets
- /// the location of the report to be the last location in the main source
- /// file.
- void resetDiagnosticLocationToMainFile();
-
- StringRef getVerboseDescription() const { return VerboseDesc; }
-
- StringRef getShortDescription() const {
- return ShortDesc.empty() ? VerboseDesc : ShortDesc;
- }
-
- StringRef getCheckName() const { return CheckName; }
- StringRef getBugType() const { return BugType; }
- StringRef getCategory() const { return Category; }
-
- /// Return the semantic context where an issue occurred. If the
- /// issue occurs along a path, this represents the "central" area
- /// where the bug manifests.
- const Decl *getDeclWithIssue() const { return DeclWithIssue; }
-
- using meta_iterator = std::deque<std::string>::const_iterator;
-
- meta_iterator meta_begin() const { return OtherDesc.begin(); }
- meta_iterator meta_end() const { return OtherDesc.end(); }
- void addMeta(StringRef s) { OtherDesc.push_back(s); }
-
- const FilesToLineNumsMap &getExecutedLines() const {
- return *ExecutedLines;
- }
-
- FilesToLineNumsMap &getExecutedLines() {
- return *ExecutedLines;
- }
-
- PathDiagnosticLocation getLocation() const {
- return Loc;
- }
-
- /// Get the location on which the report should be uniqued.
- PathDiagnosticLocation getUniqueingLoc() const {
- return UniqueingLoc;
- }
-
- /// Get the declaration containing the uniqueing location.
- const Decl *getUniqueingDecl() const {
- return UniqueingDecl;
- }
-
- void flattenLocations() {
- Loc.flatten();
- for (const auto &I : pathImpl)
- I->flattenLocations();
- }
-
- /// Profiles the diagnostic, independent of the path it references.
- ///
- /// This can be used to merge diagnostics that refer to the same issue
- /// along different paths.
- void Profile(llvm::FoldingSetNodeID &ID) const;
-
- /// Profiles the diagnostic, including its path.
- ///
- /// Two diagnostics with the same issue along different paths will generate
- /// different profiles.
- void FullProfile(llvm::FoldingSetNodeID &ID) const;
-};
-
-} // namespace ento
-
-} // namespace clang
-
-#endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_PATHDIAGNOSTIC_H
diff --git a/include/clang/StaticAnalyzer/Core/Checker.h b/include/clang/StaticAnalyzer/Core/Checker.h
index d0fe15f8b896..0c7acdbc3a97 100644
--- a/include/clang/StaticAnalyzer/Core/Checker.h
+++ b/include/clang/StaticAnalyzer/Core/Checker.h
@@ -490,12 +490,12 @@ public:
} // end eval namespace
class CheckerBase : public ProgramPointTag {
- CheckName Name;
+ CheckerNameRef Name;
friend class ::clang::ento::CheckerManager;
public:
StringRef getTagDescription() const override;
- CheckName getCheckName() const;
+ CheckerNameRef getCheckerName() const;
/// See CheckerManager::runCheckersForPrintState.
virtual void printState(raw_ostream &Out, ProgramStateRef State,
diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h
index 6cc4baa1687f..38a9aaf72c27 100644
--- a/include/clang/StaticAnalyzer/Core/CheckerManager.h
+++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h
@@ -90,21 +90,23 @@ enum PointerEscapeKind {
PSK_EscapeOther
};
-// This wrapper is used to ensure that only StringRefs originating from the
-// CheckerRegistry are used as check names. We want to make sure all check
-// name strings have a lifetime that keeps them alive at least until the path
-// diagnostics have been processed.
-class CheckName {
+/// This wrapper is used to ensure that only StringRefs originating from the
+/// CheckerRegistry are used as check names. We want to make sure all checker
+/// name strings have a lifetime that keeps them alive at least until the path
+/// diagnostics have been processed, since they are expected to be constexpr
+/// string literals (most likely generated by TblGen).
+class CheckerNameRef {
friend class ::clang::ento::CheckerRegistry;
StringRef Name;
- explicit CheckName(StringRef Name) : Name(Name) {}
+ explicit CheckerNameRef(StringRef Name) : Name(Name) {}
public:
- CheckName() = default;
+ CheckerNameRef() = default;
StringRef getName() const { return Name; }
+ operator StringRef() const { return Name; }
};
enum class ObjCMessageVisitKind {
@@ -117,7 +119,7 @@ class CheckerManager {
ASTContext &Context;
const LangOptions LangOpts;
AnalyzerOptions &AOptions;
- CheckName CurrentCheckName;
+ CheckerNameRef CurrentCheckerName;
public:
CheckerManager(ASTContext &Context, AnalyzerOptions &AOptions)
@@ -125,8 +127,8 @@ public:
~CheckerManager();
- void setCurrentCheckName(CheckName name) { CurrentCheckName = name; }
- CheckName getCurrentCheckName() const { return CurrentCheckName; }
+ void setCurrentCheckerName(CheckerNameRef name) { CurrentCheckerName = name; }
+ CheckerNameRef getCurrentCheckerName() const { return CurrentCheckerName; }
bool hasPathSensitiveCheckers() const;
@@ -162,7 +164,7 @@ public:
assert(!ref && "Checker already registered, use getChecker!");
CHECKER *checker = new CHECKER(std::forward<AT>(Args)...);
- checker->Name = CurrentCheckName;
+ checker->Name = CurrentCheckerName;
CheckerDtors.push_back(CheckerDtor(checker, destruct<CHECKER>));
CHECKER::_register(checker, *this);
ref = checker;
diff --git a/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h b/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h
index ef6e7e0f45d5..8601966c91e5 100644
--- a/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h
+++ b/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h
@@ -20,17 +20,19 @@ namespace clang {
class AnalyzerOptions;
class Preprocessor;
+namespace cross_tu {
+class CrossTranslationUnitContext;
+}
namespace ento {
class PathDiagnosticConsumer;
typedef std::vector<PathDiagnosticConsumer*> PathDiagnosticConsumers;
-#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN)\
-void CREATEFN(AnalyzerOptions &AnalyzerOpts,\
- PathDiagnosticConsumers &C,\
- const std::string &Prefix,\
- const Preprocessor &PP);
+#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN) \
+ void CREATEFN(AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, \
+ const std::string &Prefix, const Preprocessor &PP, \
+ const cross_tu::CrossTranslationUnitContext &CTU);
#include "clang/StaticAnalyzer/Core/Analyses.def"
} // end 'ento' namespace
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h
index b0dda78a00a9..d605a6a667f6 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h
@@ -15,9 +15,9 @@
#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_ANALYSISMANAGER_H
#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
-#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
namespace clang {
@@ -32,7 +32,6 @@ class AnalysisManager : public BugReporterData {
AnalysisDeclContextManager AnaCtxMgr;
ASTContext &Ctx;
- DiagnosticsEngine &Diags;
const LangOptions &LangOpts;
PathDiagnosticConsumers PathConsumers;
@@ -45,7 +44,7 @@ class AnalysisManager : public BugReporterData {
public:
AnalyzerOptions &options;
- AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags,
+ AnalysisManager(ASTContext &ctx,
const PathDiagnosticConsumers &Consumers,
StoreManagerCreator storemgr,
ConstraintManagerCreator constraintmgr,
@@ -84,10 +83,6 @@ public:
return getASTContext().getSourceManager();
}
- DiagnosticsEngine &getDiagnostic() override {
- return Diags;
- }
-
const LangOptions &getLangOpts() const {
return LangOpts;
}
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
index db84102983af..fc1cc9138826 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -347,7 +347,7 @@ public:
ProgramStateRef invalidateRegions(unsigned BlockCount,
ProgramStateRef Orig = nullptr) const;
- using FrameBindingTy = std::pair<Loc, SVal>;
+ using FrameBindingTy = std::pair<SVal, SVal>;
using BindingsTy = SmallVectorImpl<FrameBindingTy>;
/// Populates the given SmallVector with the bindings in the callee's stack
@@ -386,11 +386,12 @@ public:
/// during analysis if the call is inlined, but it may still be useful
/// in intermediate calculations even if the call isn't inlined.
/// May fail; returns null on failure.
- const StackFrameContext *getCalleeStackFrame() const;
+ const StackFrameContext *getCalleeStackFrame(unsigned BlockCount) const;
/// Returns memory location for a parameter variable within the callee stack
/// frame. May fail; returns null on failure.
- const VarRegion *getParameterLocation(unsigned Index) const;
+ const VarRegion *getParameterLocation(unsigned Index,
+ unsigned BlockCount) const;
/// Returns true if on the current path, the argument was constructed by
/// calling a C++ constructor over it. This is an internal detail of the
@@ -1063,8 +1064,19 @@ class CallDescription {
// e.g. "{a, b}" represent the qualified names, like "a::b".
std::vector<const char *> QualifiedName;
Optional<unsigned> RequiredArgs;
+ Optional<size_t> RequiredParams;
int Flags;
+ // A constructor helper.
+ static Optional<size_t> readRequiredParams(Optional<unsigned> RequiredArgs,
+ Optional<size_t> RequiredParams) {
+ if (RequiredParams)
+ return RequiredParams;
+ if (RequiredArgs)
+ return static_cast<size_t>(*RequiredArgs);
+ return None;
+ }
+
public:
/// Constructs a CallDescription object.
///
@@ -1077,14 +1089,17 @@ public:
/// call. Omit this parameter to match every occurrence of call with a given
/// name regardless the number of arguments.
CallDescription(int Flags, ArrayRef<const char *> QualifiedName,
- Optional<unsigned> RequiredArgs = None)
+ Optional<unsigned> RequiredArgs = None,
+ Optional<size_t> RequiredParams = None)
: QualifiedName(QualifiedName), RequiredArgs(RequiredArgs),
+ RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)),
Flags(Flags) {}
/// Construct a CallDescription with default flags.
CallDescription(ArrayRef<const char *> QualifiedName,
- Optional<unsigned> RequiredArgs = None)
- : CallDescription(0, QualifiedName, RequiredArgs) {}
+ Optional<unsigned> RequiredArgs = None,
+ Optional<size_t> RequiredParams = None)
+ : CallDescription(0, QualifiedName, RequiredArgs, RequiredParams) {}
/// Get the name of the function that this object matches.
StringRef getFunctionName() const { return QualifiedName.back(); }
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
index 981133e66977..7f4df0d88def 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
@@ -103,7 +103,7 @@ public:
return Eng.getBugReporter();
}
- SourceManager &getSourceManager() {
+ const SourceManager &getSourceManager() {
return getBugReporter().getSourceManager();
}
@@ -234,7 +234,7 @@ public:
}
/// A shorthand version of getNoteTag that doesn't require you to accept
- /// the BugReporterContext arguments when you don't need it.
+ /// the 'BugReporterContext' argument when you don't need it.
///
/// @param Cb Callback only with 'BugReport &' parameter.
/// @param IsPrunable Whether the note is prunable. It allows BugReporter
@@ -247,6 +247,19 @@ public:
IsPrunable);
}
+ /// A shorthand version of getNoteTag that doesn't require you to accept
+ /// the arguments when you don't need it.
+ ///
+ /// @param Cb Callback without parameters.
+ /// @param IsPrunable Whether the note is prunable. It allows BugReporter
+ /// to omit the note from the report if it would make the displayed
+ /// bug path significantly shorter.
+ const NoteTag *getNoteTag(std::function<std::string()> &&Cb,
+ bool IsPrunable = false) {
+ return getNoteTag([Cb](BugReporterContext &, BugReport &) { return Cb(); },
+ IsPrunable);
+ }
+
/// A shorthand version of getNoteTag that accepts a plain note.
///
/// @param Note The note.
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h
new file mode 100644
index 000000000000..f5a710c77a6a
--- /dev/null
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h
@@ -0,0 +1,55 @@
+//===- DynamicCastInfo.h - Runtime cast information -------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICCASTINFO_H
+#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICCASTINFO_H
+
+#include "clang/AST/Type.h"
+
+namespace clang {
+namespace ento {
+
+class DynamicCastInfo {
+public:
+ enum CastResult { Success, Failure };
+
+ DynamicCastInfo(QualType from, QualType to, CastResult resultKind)
+ : From(from), To(to), ResultKind(resultKind) {}
+
+ QualType from() const { return From; }
+ QualType to() const { return To; }
+
+ bool equals(QualType from, QualType to) const {
+ return From == from && To == to;
+ }
+
+ bool succeeds() const { return ResultKind == CastResult::Success; }
+ bool fails() const { return ResultKind == CastResult::Failure; }
+
+ bool operator==(const DynamicCastInfo &RHS) const {
+ return From == RHS.From && To == RHS.To;
+ }
+ bool operator<(const DynamicCastInfo &RHS) const {
+ return From < RHS.From && To < RHS.To;
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.Add(From);
+ ID.Add(To);
+ ID.AddInteger(ResultKind);
+ }
+
+private:
+ QualType From, To;
+ CastResult ResultKind;
+};
+
+} // namespace ento
+} // namespace clang
+
+#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICCASTINFO_H
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h
new file mode 100644
index 000000000000..356401d77561
--- /dev/null
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h
@@ -0,0 +1,73 @@
+//===- DynamicType.h - Dynamic type related APIs ----------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines APIs that track and query dynamic type information. This
+// information can be used to devirtualize calls during the symbolic execution
+// or do type checking.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPE_H
+#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPE_H
+
+#include "clang/AST/Type.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/Optional.h"
+#include <utility>
+
+namespace clang {
+namespace ento {
+
+/// Get dynamic type information for the region \p MR.
+DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR);
+
+/// Get raw dynamic type information for the region \p MR.
+const DynamicTypeInfo *getRawDynamicTypeInfo(ProgramStateRef State,
+ const MemRegion *MR);
+
+/// Get dynamic cast information from \p CastFromTy to \p CastToTy of \p MR.
+const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State,
+ const MemRegion *MR,
+ QualType CastFromTy,
+ QualType CastToTy);
+
+/// Set dynamic type information of the region; return the new state.
+ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR,
+ DynamicTypeInfo NewTy);
+
+/// Set dynamic type information of the region; return the new state.
+ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR,
+ QualType NewTy, bool CanBeSubClassed = true);
+
+/// Set dynamic type and cast information of the region; return the new state.
+ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State,
+ const MemRegion *MR,
+ QualType CastFromTy,
+ QualType CastToTy,
+ bool IsCastSucceeds);
+
+/// Removes the dead type informations from \p State.
+ProgramStateRef removeDeadTypes(ProgramStateRef State, SymbolReaper &SR);
+
+/// Removes the dead cast informations from \p State.
+ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR);
+
+void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL = "\n", unsigned int Space = 0,
+ bool IsDot = false);
+
+} // namespace ento
+} // namespace clang
+
+#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPE_H
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
index 9bb1e2137566..6262c4a1ce37 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
@@ -1,10 +1,11 @@
-//== DynamicTypeInfo.h - Runtime type information ----------------*- C++ -*--=//
+//===- DynamicTypeInfo.h - Runtime type information -------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
+
#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEINFO_H
#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEINFO_H
@@ -16,36 +17,37 @@ namespace ento {
/// Stores the currently inferred strictest bound on the runtime type
/// of a region in a given state along the analysis path.
class DynamicTypeInfo {
-private:
- QualType T;
- bool CanBeASubClass;
-
public:
+ DynamicTypeInfo() : DynTy(QualType()) {}
- DynamicTypeInfo() : T(QualType()) {}
- DynamicTypeInfo(QualType WithType, bool CanBeSub = true)
- : T(WithType), CanBeASubClass(CanBeSub) {}
+ DynamicTypeInfo(QualType Ty, bool CanBeSub = true)
+ : DynTy(Ty), CanBeASubClass(CanBeSub) {}
+
+ /// Returns false if the type information is precise (the type 'DynTy' is
+ /// the only type in the lattice), true otherwise.
+ bool canBeASubClass() const { return CanBeASubClass; }
- /// Return false if no dynamic type info is available.
- bool isValid() const { return !T.isNull(); }
+ /// Returns true if the dynamic type info is available.
+ bool isValid() const { return !DynTy.isNull(); }
/// Returns the currently inferred upper bound on the runtime type.
- QualType getType() const { return T; }
+ QualType getType() const { return DynTy; }
- /// Returns false if the type information is precise (the type T is
- /// the only type in the lattice), true otherwise.
- bool canBeASubClass() const { return CanBeASubClass; }
+ bool operator==(const DynamicTypeInfo &RHS) const {
+ return DynTy == RHS.DynTy && CanBeASubClass == RHS.CanBeASubClass;
+ }
void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.Add(T);
- ID.AddInteger((unsigned)CanBeASubClass);
- }
- bool operator==(const DynamicTypeInfo &X) const {
- return T == X.T && CanBeASubClass == X.CanBeASubClass;
+ ID.Add(DynTy);
+ ID.AddBoolean(CanBeASubClass);
}
+
+private:
+ QualType DynTy;
+ bool CanBeASubClass;
};
-} // end ento
-} // end clang
+} // namespace ento
+} // namespace clang
-#endif
+#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEINFO_H
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h b/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h
deleted file mode 100644
index a84b24872061..000000000000
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h
+++ /dev/null
@@ -1,63 +0,0 @@
-//===- DynamicTypeMap.h - Dynamic type map ----------------------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-//
-// This file provides APIs for tracking dynamic type information.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEMAP_H
-#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEMAP_H
-
-#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
-#include "llvm/ADT/ImmutableMap.h"
-#include "clang/AST/Type.h"
-
-namespace clang {
-namespace ento {
-
-class MemRegion;
-
-/// The GDM component containing the dynamic type info. This is a map from a
-/// symbol to its most likely type.
-struct DynamicTypeMap {};
-
-using DynamicTypeMapTy = llvm::ImmutableMap<const MemRegion *, DynamicTypeInfo>;
-
-template <>
-struct ProgramStateTrait<DynamicTypeMap>
- : public ProgramStatePartialTrait<DynamicTypeMapTy> {
- static void *GDMIndex();
-};
-
-/// Get dynamic type information for a region.
-DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State,
- const MemRegion *Reg);
-
-/// Set dynamic type information of the region; return the new state.
-ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg,
- DynamicTypeInfo NewTy);
-
-/// Set dynamic type information of the region; return the new state.
-inline ProgramStateRef setDynamicTypeInfo(ProgramStateRef State,
- const MemRegion *Reg, QualType NewTy,
- bool CanBeSubClassed = true) {
- return setDynamicTypeInfo(State, Reg,
- DynamicTypeInfo(NewTy, CanBeSubClassed));
-}
-
-void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State,
- const char *NL = "\n", unsigned int Space = 0,
- bool IsDot = false);
-
-} // namespace ento
-} // namespace clang
-
-#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPEMAP_H
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h
index 727d04cba278..e87772c04b9b 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h
@@ -131,10 +131,12 @@ class ExplodedNode : public llvm::FoldingSetNode {
/// Succs - The successors of this node.
NodeGroup Succs;
+ int64_t Id;
+
public:
explicit ExplodedNode(const ProgramPoint &loc, ProgramStateRef state,
- bool IsSink)
- : Location(loc), State(std::move(state)), Succs(IsSink) {
+ int64_t Id, bool IsSink)
+ : Location(loc), State(std::move(state)), Succs(IsSink), Id(Id) {
assert(isSink() == IsSink);
}
@@ -153,7 +155,11 @@ public:
CFG &getCFG() const { return *getLocationContext()->getCFG(); }
- ParentMap &getParentMap() const {return getLocationContext()->getParentMap();}
+ const CFGBlock *getCFGBlock() const;
+
+ const ParentMap &getParentMap() const {
+ return getLocationContext()->getParentMap();
+ }
template <typename T>
T &getAnalysis() const {
@@ -219,12 +225,20 @@ public:
// Iterators over successor and predecessor vertices.
using succ_iterator = ExplodedNode * const *;
+ using succ_range = llvm::iterator_range<succ_iterator>;
+
using const_succ_iterator = const ExplodedNode * const *;
+ using const_succ_range = llvm::iterator_range<const_succ_iterator>;
+
using pred_iterator = ExplodedNode * const *;
+ using pred_range = llvm::iterator_range<pred_iterator>;
+
using const_pred_iterator = const ExplodedNode * const *;
+ using const_pred_range = llvm::iterator_range<const_pred_iterator>;
pred_iterator pred_begin() { return Preds.begin(); }
pred_iterator pred_end() { return Preds.end(); }
+ pred_range preds() { return {Preds.begin(), Preds.end()}; }
const_pred_iterator pred_begin() const {
return const_cast<ExplodedNode*>(this)->pred_begin();
@@ -232,9 +246,11 @@ public:
const_pred_iterator pred_end() const {
return const_cast<ExplodedNode*>(this)->pred_end();
}
+ const_pred_range preds() const { return {Preds.begin(), Preds.end()}; }
succ_iterator succ_begin() { return Succs.begin(); }
succ_iterator succ_end() { return Succs.end(); }
+ succ_range succs() { return {Succs.begin(), Succs.end()}; }
const_succ_iterator succ_begin() const {
return const_cast<ExplodedNode*>(this)->succ_begin();
@@ -242,8 +258,9 @@ public:
const_succ_iterator succ_end() const {
return const_cast<ExplodedNode*>(this)->succ_end();
}
+ const_succ_range succs() const { return {Succs.begin(), Succs.end()}; }
- int64_t getID(ExplodedGraph *G) const;
+ int64_t getID() const { return Id; }
/// The node is trivial if it has only one successor, only one predecessor,
/// it's predecessor has only one successor,
@@ -252,6 +269,30 @@ public:
/// Trivial nodes may be skipped while printing exploded graph.
bool isTrivial() const;
+ /// If the node's program point corresponds to a statement, retrieve that
+ /// statement. Useful for figuring out where to put a warning or a note.
+ /// If the statement belongs to a body-farmed definition,
+ /// retrieve the call site for that definition.
+ const Stmt *getStmtForDiagnostics() const;
+
+ /// Find the next statement that was executed on this node's execution path.
+ /// Useful for explaining control flow that follows the current node.
+ /// If the statement belongs to a body-farmed definition, retrieve the
+ /// call site for that definition.
+ const Stmt *getNextStmtForDiagnostics() const;
+
+ /// Find the statement that was executed immediately before this node.
+ /// Useful when the node corresponds to a CFG block entrance.
+ /// If the statement belongs to a body-farmed definition, retrieve the
+ /// call site for that definition.
+ const Stmt *getPreviousStmtForDiagnostics() const;
+
+ /// Find the statement that was executed at or immediately before this node.
+ /// Useful when any nearby statement will do.
+ /// If the statement belongs to a body-farmed definition, retrieve the
+ /// call site for that definition.
+ const Stmt *getCurrentOrPreviousStmtForDiagnostics() const;
+
private:
void replaceSuccessor(ExplodedNode *node) { Succs.replaceNode(node); }
void replacePredecessor(ExplodedNode *node) { Preds.replaceNode(node); }
@@ -285,7 +326,7 @@ protected:
BumpVectorContext BVC;
/// NumNodes - The number of nodes in the graph.
- unsigned NumNodes = 0;
+ int64_t NumNodes = 0;
/// A list of recently allocated nodes that can potentially be recycled.
NodeVector ChangedNodes;
@@ -319,10 +360,11 @@ public:
/// ExplodedGraph for further processing.
ExplodedNode *createUncachedNode(const ProgramPoint &L,
ProgramStateRef State,
+ int64_t Id,
bool IsSink = false);
std::unique_ptr<ExplodedGraph> MakeEmptyGraph() const {
- return llvm::make_unique<ExplodedGraph>();
+ return std::make_unique<ExplodedGraph>();
}
/// addRoot - Add an untyped node to the set of roots.
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 2629d7121de4..2d0967616ff2 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -145,9 +145,9 @@ private:
ObjCNoReturn ObjCNoRet;
/// The BugReporter associated with this engine. It is important that
- /// this object be placed at the very end of member variables so that its
- /// destructor is called before the rest of the ExprEngine is destroyed.
- GRBugReporter BR;
+ /// this object be placed at the very end of member variables so that its
+ /// destructor is called before the rest of the ExprEngine is destroyed.
+ PathSensitiveBugReporter BR;
/// The functions which have been analyzed through inlining. This is owned by
/// AnalysisConsumer. It can be null.
@@ -530,7 +530,7 @@ public:
void VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest,
const Stmt *S, bool IsBaseDtor,
ExplodedNode *Pred, ExplodedNodeSet &Dst,
- const EvalCallOptions &Options);
+ EvalCallOptions &Options);
void VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
ExplodedNode *Pred,
@@ -666,7 +666,7 @@ public:
const LocationContext *LCtx,
ProgramStateRef State);
- /// Evaluate a call, running pre- and post-call checks and allowing checkers
+ /// Evaluate a call, running pre- and post-call checkers and allowing checkers
/// to be responsible for handling the evaluation of the call itself.
void evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
const CallEvent &Call);
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
index 071e35085a5f..71cbbe28fc25 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
@@ -169,6 +169,7 @@ public:
Kind getKind() const { return kind; }
template<typename RegionTy> const RegionTy* getAs() const;
+ template<typename RegionTy> const RegionTy* castAs() const;
virtual bool isBoundable() const { return false; }
@@ -1231,6 +1232,11 @@ const RegionTy* MemRegion::getAs() const {
return nullptr;
}
+template<typename RegionTy>
+const RegionTy* MemRegion::castAs() const {
+ return cast<RegionTy>(this);
+}
+
//===----------------------------------------------------------------------===//
// MemRegionManager - Factory object for creating regions.
//===----------------------------------------------------------------------===//
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
index d38058f9af56..07920790c80a 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
@@ -507,6 +507,10 @@ public:
return *svalBuilder;
}
+ const SValBuilder &getSValBuilder() const {
+ return *svalBuilder;
+ }
+
SymbolManager &getSymbolManager() {
return svalBuilder->getSymbolManager();
}