[clang] [-Wcompletion-handler] Fix a non-termination issue (PR #78380)
https://github.com/SavchenkoValeriy approved this pull request. Awesome! Thank you for fixing the problem and for describing it so in detail. https://github.com/llvm/llvm-project/pull/78380 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [-Wcompletion-handler] Fix a non-termination issue (PR #78380)
SavchenkoValeriy wrote: Hey @ziqingluo-90, thank you for such a detailed description of the problem. It helped me to get up to speed quickly. However, I must say that I find this change too intrusive a this point, while not being 100% convinced that it is necessary. Considering these rules (even be it for joining multiple paths), we shouldn't override `Reported` with `Escaped`. At the same time, we do that within a single block thanks to this code: https://github.com/llvm/llvm-project/blob/5fcf907b34355980f77d7665a175b05fea7a6b7b/clang/lib/Analysis/CalledOnceCheck.cpp#L935 We do consider `Reported` to be an error state, and overwrite it. Maybe changing this logic and excluding `Reported` from that condition (i.e. `x.isErrorStatus() && x != Reported`) is the way to go? The reason for that is that we already found a problem on that path and the fact that it escaped before it shouldn't affect our conclusion. Another reason is more formal, `Reported` is supposed to be a top element of the lattice and this logic defies it. Thanks again! https://github.com/llvm/llvm-project/pull/78380 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 9e02f58 - [analyzer] Highlight arrows for currently selected event
Author: Valeriy Savchenko Date: 2021-08-02T19:15:01+03:00 New Revision: 9e02f58780ab8734e5d27a0138bd477d18ae64a1 URL: https://github.com/llvm/llvm-project/commit/9e02f58780ab8734e5d27a0138bd477d18ae64a1 DIFF: https://github.com/llvm/llvm-project/commit/9e02f58780ab8734e5d27a0138bd477d18ae64a1.diff LOG: [analyzer] Highlight arrows for currently selected event In some cases, when the execution path of the diagnostic goes back and forth, arrows can overlap and create a mess. Dimming arrows that are not relevant at the moment, solves this issue. They are still visible, but don't draw too much attention. Differential Revision: https://reviews.llvm.org/D92928 Added: Modified: clang/lib/Rewrite/HTMLRewrite.cpp clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp clang/test/Analysis/html_diagnostics/control-arrows.cpp Removed: diff --git a/clang/lib/Rewrite/HTMLRewrite.cpp b/clang/lib/Rewrite/HTMLRewrite.cpp index 496b8c575dfcb..371557a624c9c 100644 --- a/clang/lib/Rewrite/HTMLRewrite.cpp +++ b/clang/lib/Rewrite/HTMLRewrite.cpp @@ -392,7 +392,7 @@ h1 { font-size:14pt } .CodeInsertionHint { font-weight: bold; background-color: #10dd10 } .CodeRemovalHint { background-color:#de1010 } .CodeRemovalHint { border-bottom:1px solid #6F9DBE } -.selected{ background-color:orange !important; } +.msg.selected{ background-color:orange !important; } table.simpletable { padding: 5px; diff --git a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index f301378c02d6c..3ee12c0bdf651 100644 --- a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -27,6 +27,7 @@ #include "clang/Rewrite/Core/Rewriter.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" @@ -58,6 +59,8 @@ using namespace ento; namespace { +class ArrowMap; + class HTMLDiagnostics : public PathDiagnosticConsumer { PathDiagnosticConsumerOptions DiagOpts; std::string Directory; @@ -119,7 +122,8 @@ class HTMLDiagnostics : public PathDiagnosticConsumer { } private: - void addArrowSVGs(Rewriter , FileID BugFileID, unsigned NumberOfArrows); + void addArrowSVGs(Rewriter , FileID BugFileID, +const ArrowMap ); /// \return Javascript for displaying shortcuts help; StringRef showHelpJavascript(); @@ -150,6 +154,20 @@ unsigned getPathSizeWithoutArrows(const PathPieces ) { return TotalPieces - TotalArrowPieces; } +class ArrowMap : public std::vector { + using Base = std::vector; + +public: + ArrowMap(unsigned Size) : Base(Size, 0) {} + unsigned getTotalNumberOfArrows() const { return at(0); } +}; + +llvm::raw_ostream <<(llvm::raw_ostream , const ArrowMap ) { + OS << "[ "; + llvm::interleave(Indices, OS, ","); + return OS << " ]"; +} + } // namespace void ento::createHTMLDiagnosticConsumer( @@ -761,6 +779,7 @@ void HTMLDiagnostics::RewriteFile(Rewriter , const PathPieces , unsigned NumberOfArrows = 0; // Stores the count of the regular piece indices. std::map IndexMap; + ArrowMap ArrowIndices(TotalRegularPieces + 1); // Stores the diff erent ranges where we have reported something. std::vector PopUpRanges; @@ -779,13 +798,30 @@ void HTMLDiagnostics::RewriteFile(Rewriter , const PathPieces , } else if (isArrowPiece(Piece)) { NumberOfArrows = ProcessControlFlowPiece( R, FID, cast(Piece), NumberOfArrows); + ArrowIndices[NumRegularPieces] = NumberOfArrows; } else { HandlePiece(R, FID, Piece, PopUpRanges, NumRegularPieces, TotalRegularPieces); --NumRegularPieces; + ArrowIndices[NumRegularPieces] = ArrowIndices[NumRegularPieces + 1]; } } + ArrowIndices[0] = NumberOfArrows; + + // At this point ArrowIndices represent the following data structure: + // [a_0, a_1, ..., a_N] + // where N is the number of events in the path. + // + // Then for every event with index i \in [0, N - 1], we can say that + // arrows with indices \in [a_(i+1), a_i) correspond to that event. + // We can say that because arrows with these indices appeared in the + // path in between the i-th and the (i+1)-th events. + assert(ArrowIndices.back() == 0 && + "No arrows should be after the last event"); + // This assertion also guarantees that all indices in are <= NumberOfArrows. + assert(llvm::is_sorted(ArrowIndices, std::greater()) && + "Incorrect arrow indices map"); // Secondary indexing if we are having multiple pop-ups between two notes. // (e.g. [(13) 'a' is 'true']; [(13.1) 'b' is 'false']; [(13.2) 'c' is...) @@ -819,7 +855,7 @@ void HTMLDiagnostics::RewriteFile(Rewriter , const PathPieces ,
[clang] 97bcafa - [analyzer] Add control flow arrows to the analyzer's HTML reports
Author: Valeriy Savchenko Date: 2021-08-02T19:15:00+03:00 New Revision: 97bcafa28deb95ad32f83fe339d78454d899ca1b URL: https://github.com/llvm/llvm-project/commit/97bcafa28deb95ad32f83fe339d78454d899ca1b DIFF: https://github.com/llvm/llvm-project/commit/97bcafa28deb95ad32f83fe339d78454d899ca1b.diff LOG: [analyzer] Add control flow arrows to the analyzer's HTML reports This commit adds a very first version of this feature. It is off by default and has to be turned on by checking the corresponding box. For this reason, HTML reports still keep control notes (aka grey bubbles). Further on, we plan on attaching arrows to events and having all arrows not related to a currently selected event barely visible. This will help with reports where control flow goes back and forth (eg in loops). Right now, it can get pretty crammed with all the arrows. Differential Revision: https://reviews.llvm.org/D92639 Added: clang/test/Analysis/html_diagnostics/control-arrows.cpp Modified: clang/include/clang/Analysis/PathDiagnostic.h clang/lib/Rewrite/HTMLRewrite.cpp clang/lib/StaticAnalyzer/Core/BugReporter.cpp clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp Removed: diff --git a/clang/include/clang/Analysis/PathDiagnostic.h b/clang/include/clang/Analysis/PathDiagnostic.h index 539aa20b81682..04bef1fa5334d 100644 --- a/clang/include/clang/Analysis/PathDiagnostic.h +++ b/clang/include/clang/Analysis/PathDiagnostic.h @@ -151,11 +151,14 @@ class PathDiagnosticConsumer { /// Only runs visitors, no output generated. None, -/// Used for HTML, SARIF, and text output. +/// Used for SARIF and text output. Minimal, /// Used for plist output, used for "arrows" generation. Extensive, + +/// Used for HTML, shows both "arrows" and control notes. +Everything }; virtual PathGenerationScheme getGenerationScheme() const { return Minimal; } @@ -164,7 +167,11 @@ class PathDiagnosticConsumer { return getGenerationScheme() != None; } - bool shouldAddPathEdges() const { return getGenerationScheme() == Extensive; } + bool shouldAddPathEdges() const { return getGenerationScheme() >= Extensive; } + bool shouldAddControlNotes() const { +return getGenerationScheme() == Minimal || + getGenerationScheme() == Everything; + } virtual bool supportsLogicalOpControlFlow() const { return false; } diff --git a/clang/lib/Rewrite/HTMLRewrite.cpp b/clang/lib/Rewrite/HTMLRewrite.cpp index 2f5f2734aa46b..496b8c575dfcb 100644 --- a/clang/lib/Rewrite/HTMLRewrite.cpp +++ b/clang/lib/Rewrite/HTMLRewrite.cpp @@ -371,6 +371,7 @@ h1 { font-size:14pt } .msg { border-radius:5px } .msg { font-family:Helvetica, sans-serif; font-size:8pt } .msg { float:left } +.msg { position:relative } .msg { padding:0.25em 1ex 0.25em 1ex } .msg { margin-top:10px; margin-bottom:10px } .msg { font-weight:bold } diff --git a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp index d6f69ae03afe5..da441dcdf0183 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -188,6 +188,9 @@ class PathDiagnosticConstruct { PathPieces () { return PD->getMutablePieces(); } bool shouldAddPathEdges() const { return Consumer->shouldAddPathEdges(); } + bool shouldAddControlNotes() const { +return Consumer->shouldAddControlNotes(); + } bool shouldGenerateDiagnostics() const { return Consumer->shouldGenerateDiagnostics(); } @@ -1232,8 +1235,11 @@ void PathDiagnosticBuilder::generatePathDiagnosticsForNode( } else if (auto BE = P.getAs()) { -if (!C.shouldAddPathEdges()) { +if (C.shouldAddControlNotes()) { generateMinimalDiagForBlockEdge(C, *BE); +} + +if (!C.shouldAddPathEdges()) { return; } @@ -1254,12 +1260,14 @@ void PathDiagnosticBuilder::generatePathDiagnosticsForNode( // do-while statements are explicitly excluded here auto p = std::make_shared( - L, "Looping back to the head " - "of the loop"); + L, "Looping back to the head of the loop"); p->setPrunable(true); addEdgeToPath(C.getActivePath(), PrevLoc, p->getLocation()); - C.getActivePath().push_front(std::move(p)); + // We might've added a very similar control node already + if (!C.shouldAddControlNotes()) { +C.getActivePath().push_front(std::move(p)); + } if (const auto *CS = dyn_cast_or_null(Body)) { addEdgeToPath(C.getActivePath(), PrevLoc, @@ -1300,10 +1308,14 @@ void PathDiagnosticBuilder::generatePathDiagnosticsForNode( auto PE = std::make_shared(L, str); PE->setPrunable(true); addEdgeToPath(C.getActivePath(), PrevLoc, PE->getLocation()); - C.getActivePath().push_front(std::move(PE)); + + // We might've added
[clang] f26deb4 - [analyzer][solver][NFC] Introduce ConstraintAssignor
Author: Valeriy Savchenko Date: 2021-07-13T21:00:30+03:00 New Revision: f26deb4e6ba7e00c57b4be888c4d20c95a881154 URL: https://github.com/llvm/llvm-project/commit/f26deb4e6ba7e00c57b4be888c4d20c95a881154 DIFF: https://github.com/llvm/llvm-project/commit/f26deb4e6ba7e00c57b4be888c4d20c95a881154.diff LOG: [analyzer][solver][NFC] Introduce ConstraintAssignor The new component is a symmetric response to SymbolicRangeInferrer. While the latter is the unified component, which answers all the questions what does the solver knows about a particular symbolic expression, assignor associates new constraints (aka "assumes") with symbolic expressions and can imply additional knowledge that the solver can extract and use later on. - Why do we need it and why is SymbolicRangeInferrer not enough? As it is noted before, the inferrer only helps us to get the most precise range information based on the existing knowledge and on the mathematical foundations of different operations that symbolic expressions actually represent. It doesn't introduce new constraints. The assignor, on the other hand, can impose constraints on other symbols using the same domain knowledge. - But for some expressions, SymbolicRangeInferrer looks into constraints for similar expressions, why can't we do that for all the cases? That's correct! But in order to do something like this, we should have a finite number of possible "similar expressions". Let's say we are asked about `$a - $b` and we know something about `$b - $a`. The inferrer can invert this expression and check constraints for `$b - $a`. This is simple! But let's say we are asked about `$a` and we know that `$a * $b != 0`. In this situation, we can imply that `$a != 0`, but the inferrer shouldn't try every possible symbolic expression `X` to check if `$a * X` or `X * $a` is constrained to non-zero. With the assignor mechanism, we can catch this implication right at the moment we associate `$a * $b` with non-zero range, and set similar constraints for `$a` and `$b` as well. Differential Revision: https://reviews.llvm.org/D105692 Added: Modified: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index bc8c83132c5d7..803cc5efd0c1e 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -669,6 +669,17 @@ LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State, return getConstraint(State, EquivalenceClass::find(State, Sym)); } +LLVM_NODISCARD ProgramStateRef setConstraint(ProgramStateRef State, + EquivalenceClass Class, + RangeSet Constraint) { + return State->set(Class, Constraint); +} + +LLVM_NODISCARD ProgramStateRef setConstraints(ProgramStateRef State, + ConstraintRangeTy Constraints) { + return State->set(Constraints); +} + //===--===// // Equality/diseqiality abstraction //===--===// @@ -1373,6 +1384,182 @@ RangeSet SymbolicRangeInferrer::VisitBinaryOperator(Range LHS, return {RangeFactory, ValueFactory.getValue(Min), ValueFactory.getValue(Max)}; } +//===--===// +// Constraint assignment logic +//===--===// + +/// ConstraintAssignorBase is a small utility class that unifies visitor +/// for ranges with a visitor for constraints (rangeset/range/constant). +/// +/// It is designed to have one derived class, but generally it can have more. +/// Derived class can control which types we handle by defining methods of the +/// following form: +/// +/// bool handle${SYMBOL}To${CONSTRAINT}(const SYMBOL *Sym, +/// CONSTRAINT Constraint); +/// +/// where SYMBOL is the type of the symbol (e.g. SymSymExpr, SymbolCast, etc.) +/// CONSTRAINT is the type of constraint (RangeSet/Range/Const) +/// return value signifies whether we should try other handle methods +/// (i.e. false would mean to stop right after calling this method) +template class ConstraintAssignorBase { +public: + using Const = const llvm::APSInt &; + +#define DISPATCH(CLASS) return assign##CLASS##Impl(cast(Sym), Constraint) + +#define ASSIGN(CLASS, TO, SYM, CONSTRAINT) \ + if (!static_cast(this)->assign##CLASS##To##TO(SYM, CONSTRAINT)) \ + return false + + void assign(SymbolRef Sym, RangeSet Constraint) { +
[clang] 60bd8cb - [analyzer][solver][NFC] Refactor how we detect (dis)equalities
Author: Valeriy Savchenko Date: 2021-07-13T21:00:30+03:00 New Revision: 60bd8cbc0c84a41146b1ad6c832fa75f48cd2568 URL: https://github.com/llvm/llvm-project/commit/60bd8cbc0c84a41146b1ad6c832fa75f48cd2568 DIFF: https://github.com/llvm/llvm-project/commit/60bd8cbc0c84a41146b1ad6c832fa75f48cd2568.diff LOG: [analyzer][solver][NFC] Refactor how we detect (dis)equalities This patch simplifies the way we deal with (dis)equalities. Due to the symmetry between constraint handler and range inferrer, we can have very similar implementations of logic handling questions about (dis)equality and assumptions involving (dis)equality. It also helps us to remove one more visitor, and removes uncertainty that we got all the right places to put `trackNE` and `trackEQ`. Differential Revision: https://reviews.llvm.org/D105693 Added: Modified: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp clang/test/Analysis/equality_tracking.c Removed: diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 803cc5efd0c1..9e014e9fce4e 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -684,58 +684,30 @@ LLVM_NODISCARD ProgramStateRef setConstraints(ProgramStateRef State, // Equality/diseqiality abstraction //===--===// -/// A small helper structure representing symbolic equality. +/// A small helper function for detecting symbolic (dis)equality. /// /// Equality check can have diff erent forms (like a == b or a - b) and this /// class encapsulates those away if the only thing the user wants to check - -/// whether it's equality/diseqiality or not and have an easy access to the -/// compared symbols. -struct EqualityInfo { -public: - SymbolRef Left, Right; - // true for equality and false for disequality. - bool IsEquality = true; - - void invert() { IsEquality = !IsEquality; } - /// Extract equality information from the given symbol and the constants. - /// - /// This function assumes the following expression Sym + Adjustment != Int. - /// It is a default because the most widespread case of the equality check - /// is (A == B) + 0 != 0. - static Optional extract(SymbolRef Sym, const llvm::APSInt , -const llvm::APSInt ) { -// As of now, the only equality form supported is Sym + 0 != 0. -if (!Int.isNullValue() || !Adjustment.isNullValue()) - return llvm::None; - -return extract(Sym); - } - /// Extract equality information from the given symbol. - static Optional extract(SymbolRef Sym) { -return EqualityExtractor().Visit(Sym); +/// whether it's equality/diseqiality or not. +/// +/// \returns true if assuming this Sym to be true means equality of operands +/// false if it means disequality of operands +/// None otherwise +Optional meansEquality(const SymSymExpr *Sym) { + switch (Sym->getOpcode()) { + case BO_Sub: +// This case is: A - B != 0 -> disequality check. +return false; + case BO_EQ: +// This case is: A == B != 0 -> equality check. +return true; + case BO_NE: +// This case is: A != B != 0 -> diseqiality check. +return false; + default: +return llvm::None; } - -private: - class EqualityExtractor - : public SymExprVisitor> { - public: -Optional VisitSymSymExpr(const SymSymExpr *Sym) const { - switch (Sym->getOpcode()) { - case BO_Sub: -// This case is: A - B != 0 -> disequality check. -return EqualityInfo{Sym->getLHS(), Sym->getRHS(), false}; - case BO_EQ: -// This case is: A == B != 0 -> equality check. -return EqualityInfo{Sym->getLHS(), Sym->getRHS(), true}; - case BO_NE: -// This case is: A != B != 0 -> diseqiality check. -return EqualityInfo{Sym->getLHS(), Sym->getRHS(), false}; - default: -return llvm::None; - } -} - }; -}; +} //===--===// //Intersection functions @@ -866,7 +838,13 @@ class SymbolicRangeInferrer } RangeSet VisitSymSymExpr(const SymSymExpr *Sym) { -return VisitBinaryOperator(Sym); +return intersect( +RangeFactory, +// If Sym is (dis)equality, we might have some information +// on that in our equality classes data structure. +getRangeForEqualities(Sym), +// And we should always check what we can get from the operands. +VisitBinaryOperator(Sym)); } private: @@ -907,9 +885,6 @@ class SymbolicRangeInferrer // calculate the effective range set by intersecting the range set // for A - B and the negated range set of B - A.
[clang] 1af97c9 - [analyzer] LoopUnrolling: fix crash when a loop counter is captured in a lambda by reference
Author: Abbas Sabra Date: 2021-07-12T17:06:07+03:00 New Revision: 1af97c9d0b02002586473b4b9845b0c390504a27 URL: https://github.com/llvm/llvm-project/commit/1af97c9d0b02002586473b4b9845b0c390504a27 DIFF: https://github.com/llvm/llvm-project/commit/1af97c9d0b02002586473b4b9845b0c390504a27.diff LOG: [analyzer] LoopUnrolling: fix crash when a loop counter is captured in a lambda by reference Reviewed By: vsavchenko Differential Revision: https://reviews.llvm.org/D102273 Added: Modified: clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp clang/test/Analysis/loop-unrolling.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp index dc268e562237f..e5f4e9ea30c97 100644 --- a/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp +++ b/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp @@ -79,14 +79,17 @@ ProgramStateRef processLoopEnd(const Stmt *LoopStmt, ProgramStateRef State) { return State; } -static internal::Matcher simpleCondition(StringRef BindName) { - return binaryOperator(anyOf(hasOperatorName("<"), hasOperatorName(">"), - hasOperatorName("<="), hasOperatorName(">="), - hasOperatorName("!=")), -hasEitherOperand(ignoringParenImpCasts(declRefExpr( - to(varDecl(hasType(isInteger())).bind(BindName), -hasEitherOperand(ignoringParenImpCasts( -integerLiteral().bind("boundNum" +static internal::Matcher simpleCondition(StringRef BindName, + StringRef RefName) { + return binaryOperator( + anyOf(hasOperatorName("<"), hasOperatorName(">"), + hasOperatorName("<="), hasOperatorName(">="), + hasOperatorName("!=")), + hasEitherOperand(ignoringParenImpCasts( + declRefExpr(to(varDecl(hasType(isInteger())).bind(BindName))) + .bind(RefName))), + hasEitherOperand( + ignoringParenImpCasts(integerLiteral().bind("boundNum" .bind("conditionOperator"); } @@ -138,7 +141,7 @@ static internal::Matcher hasSuspiciousStmt(StringRef NodeName) { static internal::Matcher forLoopMatcher() { return forStmt( - hasCondition(simpleCondition("initVarName")), + hasCondition(simpleCondition("initVarName", "initVarRef")), // Initialization should match the form: 'int i = 6' or 'i = 42'. hasLoopInit( anyOf(declStmt(hasSingleDecl( @@ -156,17 +159,52 @@ static internal::Matcher forLoopMatcher() { hasUnaryOperand(declRefExpr( to(varDecl(allOf(equalsBoundNode("initVarName"), hasType(isInteger(), - unless(hasBody(hasSuspiciousStmt("initVarName".bind("forLoop"); + unless(hasBody(hasSuspiciousStmt("initVarName" + .bind("forLoop"); } -static bool isPossiblyEscaped(const VarDecl *VD, ExplodedNode *N) { - // Global variables assumed as escaped variables. +static bool isCapturedByReference(ExplodedNode *N, const DeclRefExpr *DR) { + + // Get the lambda CXXRecordDecl + assert(DR->refersToEnclosingVariableOrCapture()); + const LocationContext *LocCtxt = N->getLocationContext(); + const Decl *D = LocCtxt->getDecl(); + const auto *MD = cast(D); + assert(MD && MD->getParent()->isLambda() && + "Captured variable should only be seen while evaluating a lambda"); + const CXXRecordDecl *LambdaCXXRec = MD->getParent(); + + // Lookup the fields of the lambda + llvm::DenseMap LambdaCaptureFields; + FieldDecl *LambdaThisCaptureField; + LambdaCXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField); + + // Check if the counter is captured by reference + const VarDecl *VD = cast(DR->getDecl()->getCanonicalDecl()); + assert(VD); + const FieldDecl *FD = LambdaCaptureFields[VD]; + assert(FD && "Captured variable without a corresponding field"); + return FD->getType()->isReferenceType(); +} + +// A loop counter is considered escaped if: +// case 1: It is a global variable. +// case 2: It is a reference parameter or a reference capture. +// case 3: It is assigned to a non-const reference variable or parameter. +// case 4: Has its address taken. +static bool isPossiblyEscaped(ExplodedNode *N, const DeclRefExpr *DR) { + const VarDecl *VD = cast(DR->getDecl()->getCanonicalDecl()); + assert(VD); + // Case 1: if (VD->hasGlobalStorage()) return true; - const bool isParm = isa(VD); - // Reference parameters are assumed as escaped variables. - if (isParm && VD->getType()->isReferenceType()) + const bool IsRefParamOrCapture = + isa(VD) || DR->refersToEnclosingVariableOrCapture(); + // Case 2: + if
[clang] 6017cb3 - [analyzer][solver] Use all sources of constraints
Author: Valeriy Savchenko Date: 2021-07-06T11:09:08+03:00 New Revision: 6017cb31bb3548641465ea66219e11abc3106d38 URL: https://github.com/llvm/llvm-project/commit/6017cb31bb3548641465ea66219e11abc3106d38 DIFF: https://github.com/llvm/llvm-project/commit/6017cb31bb3548641465ea66219e11abc3106d38.diff LOG: [analyzer][solver] Use all sources of constraints Prior to this patch, we always gave priority to constraints that we actually know about symbols in question. However, these can get outdated and we can get better results if we look at all possible sources of knowledge, including sub-expressions. Differential Revision: https://reviews.llvm.org/D105436 Added: Modified: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp clang/test/Analysis/constant-folding.c Removed: diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 0e57a1a5040fc..bc8c83132c5d7 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -884,26 +884,28 @@ class SymbolicRangeInferrer } RangeSet infer(SymbolRef Sym) { -if (Optional ConstraintBasedRange = intersect( -RangeFactory, getConstraint(State, Sym), -// If Sym is a diff erence of symbols A - B, then maybe we have range -// set stored for B - A. -// -// If we have range set stored for both A - B and B - A then -// calculate the effective range set by intersecting the range set -// for A - B and the negated range set of B - A. -getRangeForNegatedSub(Sym), getRangeForEqualities(Sym))) { - return *ConstraintBasedRange; -} - -// If Sym is a comparison expression (except <=>), -// find any other comparisons with the same operands. -// See function description. -if (Optional CmpRangeSet = getRangeForComparisonSymbol(Sym)) { - return *CmpRangeSet; -} - -return Visit(Sym); +return intersect( +RangeFactory, +// Of course, we should take the constraint directly associated with +// this symbol into consideration. +getConstraint(State, Sym), +// If Sym is a diff erence of symbols A - B, then maybe we have range +// set stored for B - A. +// +// If we have range set stored for both A - B and B - A then +// calculate the effective range set by intersecting the range set +// for A - B and the negated range set of B - A. +getRangeForNegatedSub(Sym), +// If Sym is (dis)equality, we might have some information on that +// in our equality classes data structure. +getRangeForEqualities(Sym), +// If Sym is a comparison expression (except <=>), +// find any other comparisons with the same operands. +// See function description. +getRangeForComparisonSymbol(Sym), +// Apart from the Sym itself, we can infer quite a lot if we look +// into subexpressions of Sym. +Visit(Sym)); } RangeSet infer(EquivalenceClass Class) { diff --git a/clang/test/Analysis/constant-folding.c b/clang/test/Analysis/constant-folding.c index 08a7accfba641..116e74b746b4e 100644 --- a/clang/test/Analysis/constant-folding.c +++ b/clang/test/Analysis/constant-folding.c @@ -179,6 +179,36 @@ void testBitwiseRules(unsigned int a, int b, int c) { } } +unsigned reset(); + +void testCombinedSources(unsigned a, unsigned b) { + if (b >= 10 && (a | b) <= 30) { +// Check that we can merge constraints from (a | b), a, and b. +// Because of the order of assumptions, we already know that (a | b) is [10, 30]. +clang_analyzer_eval((a | b) >= 10 && (a | b) <= 30); // expected-warning{{TRUE}} + } + + a = reset(); + b = reset(); + + if ((a | b) <= 30 && b >= 10) { +// Check that we can merge constraints from (a | b), a, and b. +// At this point, we know that (a | b) is [0, 30], but the knowledge +// of b >= 10 added later can help us to refine it and change it to [10, 30]. +clang_analyzer_eval(10 <= (a | b) && (a | b) <= 30); // expected-warning{{TRUE}} + } + + a = reset(); + b = reset(); + + unsigned c = (a | b) & (a != b); + if (c <= 40 && a == b) { +// Even though we have a directo constraint for c [0, 40], +// we can get a more precise range by looking at the expression itself. +clang_analyzer_eval(c == 0); // expected-warning{{TRUE}} + } +} + void testRemainderRules(unsigned int a, unsigned int b, int c, int d) { // Check that we know that remainder of zero divided by any number is still 0. clang_analyzer_eval((0 % c) == 0); // expected-warning{{TRUE}} ___ cfe-commits mailing list cfe-commits@lists.llvm.org
[clang] c818cb9 - [analyzer][satest][NFC] Relax dependencies requirements
Author: Valeriy Savchenko Date: 2021-06-30T12:50:21+03:00 New Revision: c818cb96ad4aa65bceadc72199677c852e8c22bd URL: https://github.com/llvm/llvm-project/commit/c818cb96ad4aa65bceadc72199677c852e8c22bd DIFF: https://github.com/llvm/llvm-project/commit/c818cb96ad4aa65bceadc72199677c852e8c22bd.diff LOG: [analyzer][satest][NFC] Relax dependencies requirements Added: Modified: clang/utils/analyzer/Dockerfile Removed: diff --git a/clang/utils/analyzer/Dockerfile b/clang/utils/analyzer/Dockerfile index f74ff8aa95c25..bb1dd60eeb9b8 100644 --- a/clang/utils/analyzer/Dockerfile +++ b/clang/utils/analyzer/Dockerfile @@ -13,16 +13,16 @@ RUN apt-add-repository -y 'deb https://apt.kitware.com/ubuntu/ bionic main' # test system dependencies RUN apt-get update && apt-get install -y \ -git=1:2.17.1-1ubuntu0.7 \ -gettext=0.19.8.1-6ubuntu0.3 \ +git=1:2.17.1* \ +gettext=0.19.8.1* \ python3=3.6.7-1~18.04 \ -python3-pip=9.0.1-2.3~ubuntu1.18.04.1 \ -cmake=3.17.3-0kitware1 \ +python3-pip=9.0.1-2.3* \ +cmake=3.20.5* \ ninja-build=1.8.2-1 # box2d dependencies RUN apt-get install -y \ -libx11-dev=2:1.6.4-3ubuntu0.2 \ +libx11-dev=2:1.6.4-3* \ libxrandr-dev=2:1.5.1-1 \ libxinerama-dev=2:1.1.3-1 \ libxcursor-dev=1:1.1.15-1 \ @@ -35,22 +35,22 @@ RUN apt-get install -y \ # simbody dependencies RUN apt-get install -y \ -liblapack-dev=3.7.1-4ubuntu1 +liblapack-dev=3.7.1-4* # drogon dependencies RUN apt-get install -y \ -libjsonrpccpp-dev=0.7.0-1build2 \ -uuid-dev=2.31.1-0.4ubuntu3.6 +libjsonrpccpp-dev=0.7.0-1* \ +uuid-dev=2.31.1-0.4* # tmux dependencies RUN apt-get install -y \ autotools-dev=20180224.1 \ -automake=1:1.15.1-3ubuntu2 \ -libncurses5-dev=6.1-1ubuntu1.18.04 \ -libevent-dev=2.1.8-stable-4build1 \ -pkg-config=0.29.1-0ubuntu2 \ +automake=1:1.15.1-3* \ +libncurses5-dev=6.1-1* \ +libevent-dev=2.1.8* \ +pkg-config=0.29.1-0* \ flex=2.6.4-6 \ -bison=2:3.0.4.dfsg-1build1 +bison=2:3.0.4.* RUN update-alternatives --install /usr/bin/python python /usr/bin/python3 1 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] b284229 - [analyzer] Fix SValTest for LocAsInt test
Author: Valeriy Savchenko Date: 2021-06-29T13:01:41+03:00 New Revision: b2842298cebf420ecb3750bf309021a7f37870c1 URL: https://github.com/llvm/llvm-project/commit/b2842298cebf420ecb3750bf309021a7f37870c1 DIFF: https://github.com/llvm/llvm-project/commit/b2842298cebf420ecb3750bf309021a7f37870c1.diff LOG: [analyzer] Fix SValTest for LocAsInt test Added: Modified: clang/unittests/StaticAnalyzer/SValTest.cpp Removed: diff --git a/clang/unittests/StaticAnalyzer/SValTest.cpp b/clang/unittests/StaticAnalyzer/SValTest.cpp index 0956984868384..ea10d69d2804e 100644 --- a/clang/unittests/StaticAnalyzer/SValTest.cpp +++ b/clang/unittests/StaticAnalyzer/SValTest.cpp @@ -166,7 +166,7 @@ void foo(int *x) { SVal A = getByName("a"); ASSERT_FALSE(A.getType(Context).isNull()); // TODO: Turn it into signed long - EXPECT_EQ(Context.UnsignedLongTy, A.getType(Context)); + EXPECT_EQ(Context.getUIntPtrType(), A.getType(Context)); SVal B = getByName("b"); ASSERT_FALSE(B.getType(Context).isNull()); ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 159024c - [analyzer] Implement getType for SVal
Author: Valeriy Savchenko Date: 2021-06-29T12:11:19+03:00 New Revision: 159024ce231502d4d68825c35c3548a14577f0fd URL: https://github.com/llvm/llvm-project/commit/159024ce231502d4d68825c35c3548a14577f0fd DIFF: https://github.com/llvm/llvm-project/commit/159024ce231502d4d68825c35c3548a14577f0fd.diff LOG: [analyzer] Implement getType for SVal This commit adds a function to the top-class of SVal hierarchy to provide type information about the value. That can be extremely useful when this is the only piece of information that the user is actually caring about. Additionally, this commit introduces a testing framework for writing unit-tests for symbolic values. Differential Revision: https://reviews.llvm.org/D104550 Added: clang/unittests/StaticAnalyzer/SValTest.cpp Modified: clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h clang/lib/StaticAnalyzer/Core/SVals.cpp clang/unittests/StaticAnalyzer/CMakeLists.txt Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h index f59b254094db8..bb598af681666 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h @@ -52,6 +52,8 @@ class CompoundValData : public llvm::FoldingSetNode { iterator begin() const { return L.begin(); } iterator end() const { return L.end(); } + QualType getType() const { return T; } + static void Profile(llvm::FoldingSetNodeID& ID, QualType T, llvm::ImmutableList L); diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h index b1c33713febd9..6199c8d8d179c 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -201,6 +201,19 @@ class SVal { SymExpr::symbol_iterator symbol_end() const { return SymExpr::symbol_end(); } + + /// Try to get a reasonable type for the given value. + /// + /// \returns The best approximation of the value type or Null. + /// In theory, all symbolic values should be typed, but this function + /// is still a WIP and might have a few blind spots. + /// + /// \note This function should not be used when the user has access to the + /// bound expression AST node as well, since AST always has exact types. + /// + /// \note Loc values are interpreted as pointer rvalues for the purposes of + /// this method. + QualType getType(const ASTContext &) const; }; inline raw_ostream <<(raw_ostream , clang::ento::SVal V) { diff --git a/clang/lib/StaticAnalyzer/Core/SVals.cpp b/clang/lib/StaticAnalyzer/Core/SVals.cpp index 252596887e4f1..117546e43b1a1 100644 --- a/clang/lib/StaticAnalyzer/Core/SVals.cpp +++ b/clang/lib/StaticAnalyzer/Core/SVals.cpp @@ -12,6 +12,7 @@ //===--===// #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" @@ -21,6 +22,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/ADT/Optional.h" @@ -136,6 +138,63 @@ const MemRegion *SVal::getAsRegion() const { return nullptr; } +namespace { +class TypeRetrievingVisitor +: public FullSValVisitor { +private: + const ASTContext + +public: + TypeRetrievingVisitor(const ASTContext ) : Context(Context) {} + + QualType VisitLocMemRegionVal(loc::MemRegionVal MRV) { +return Visit(MRV.getRegion()); + } + QualType VisitLocGotoLabel(loc::GotoLabel GL) { +return QualType{Context.VoidPtrTy}; + } + template QualType VisitConcreteInt(ConcreteInt CI) { +const llvm::APSInt = CI.getValue(); +return Context.getIntTypeForBitwidth(Value.getBitWidth(), Value.isSigned()); + } + QualType VisitLocConcreteInt(loc::ConcreteInt CI) { +return VisitConcreteInt(CI); + } + QualType VisitNonLocConcreteInt(nonloc::ConcreteInt CI) { +return VisitConcreteInt(CI); + } + QualType VisitNonLocLocAsInteger(nonloc::LocAsInteger LI) { +QualType NestedType = Visit(LI.getLoc()); +if (NestedType.isNull()) + return NestedType; + +return Context.getIntTypeForBitwidth(LI.getNumBits(), +
[clang] 8474bb1 - [analyzer][solver][NFC] Simplify function signatures
Author: Valeriy Savchenko Date: 2021-06-28T14:20:06+03:00 New Revision: 8474bb13c3270d4195a663013b95e6065075ce56 URL: https://github.com/llvm/llvm-project/commit/8474bb13c3270d4195a663013b95e6065075ce56 DIFF: https://github.com/llvm/llvm-project/commit/8474bb13c3270d4195a663013b95e6065075ce56.diff LOG: [analyzer][solver][NFC] Simplify function signatures Since RangeSet::Factory actually contains BasicValueFactory, we can remove value factory from many function signatures inside the solver. Differential Revision: https://reviews.llvm.org/D105005 Added: Modified: clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h index bf00fd98a4616..c67df1e51b4ff 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h @@ -213,6 +213,9 @@ class RangeSet { /// where N = size(What) RangeSet negate(RangeSet What); +/// Return associated value factory. +BasicValueFactory () const { return ValueFactory; } + private: /// Return a persistent version of the given container. RangeSet makePersistent(ContainerType &); diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 27367ff5ae80c..c3d8a0a87635d 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -549,14 +549,13 @@ class EquivalenceClass : public llvm::FoldingSetNode { SymbolRef Sym); /// Merge classes for the given symbols and return a new state. - LLVM_NODISCARD static inline ProgramStateRef - merge(BasicValueFactory , RangeSet::Factory , ProgramStateRef State, -SymbolRef First, SymbolRef Second); + LLVM_NODISCARD static inline ProgramStateRef merge(RangeSet::Factory , + ProgramStateRef State, + SymbolRef First, + SymbolRef Second); // Merge this class with the given class and return a new state. - LLVM_NODISCARD inline ProgramStateRef merge(BasicValueFactory , - RangeSet::Factory , - ProgramStateRef State, - EquivalenceClass Other); + LLVM_NODISCARD inline ProgramStateRef + merge(RangeSet::Factory , ProgramStateRef State, EquivalenceClass Other); /// Return a set of class members for the given state. LLVM_NODISCARD inline SymbolSet getClassMembers(ProgramStateRef State) const; @@ -567,15 +566,14 @@ class EquivalenceClass : public llvm::FoldingSetNode { SymbolReaper ) const; LLVM_NODISCARD static inline ProgramStateRef - markDisequal(BasicValueFactory , RangeSet::Factory , - ProgramStateRef State, SymbolRef First, SymbolRef Second); + markDisequal(RangeSet::Factory , ProgramStateRef State, SymbolRef First, + SymbolRef Second); LLVM_NODISCARD static inline ProgramStateRef - markDisequal(BasicValueFactory , RangeSet::Factory , - ProgramStateRef State, EquivalenceClass First, - EquivalenceClass Second); + markDisequal(RangeSet::Factory , ProgramStateRef State, + EquivalenceClass First, EquivalenceClass Second); LLVM_NODISCARD inline ProgramStateRef - markDisequal(BasicValueFactory , RangeSet::Factory , - ProgramStateRef State, EquivalenceClass Other) const; + markDisequal(RangeSet::Factory , ProgramStateRef State, + EquivalenceClass Other) const; LLVM_NODISCARD static inline ClassSet getDisequalClasses(ProgramStateRef State, SymbolRef Sym); LLVM_NODISCARD inline ClassSet @@ -641,15 +639,13 @@ class EquivalenceClass : public llvm::FoldingSetNode { } static inline SymbolSet::Factory (ProgramStateRef State); - inline ProgramStateRef mergeImpl(BasicValueFactory , RangeSet::Factory , - ProgramStateRef State, SymbolSet Members, - EquivalenceClass Other, + inline ProgramStateRef mergeImpl(RangeSet::Factory , ProgramStateRef State, + SymbolSet Members, EquivalenceClass Other, SymbolSet OtherMembers); static inline bool addToDisequalityInfo(DisequalityMapTy , ConstraintRangeTy , -
[clang] d646157 - [analyzer] Fix assertion failure on code with transparent unions
Author: Valeriy Savchenko Date: 2021-06-25T23:09:16+03:00 New Revision: d646157146ccda93cd72dcbaff4a554c61ed9586 URL: https://github.com/llvm/llvm-project/commit/d646157146ccda93cd72dcbaff4a554c61ed9586 DIFF: https://github.com/llvm/llvm-project/commit/d646157146ccda93cd72dcbaff4a554c61ed9586.diff LOG: [analyzer] Fix assertion failure on code with transparent unions rdar://76948312 Differential Revision: https://reviews.llvm.org/D104716 Added: clang/test/Analysis/transparent_union_bug.c Modified: clang/lib/StaticAnalyzer/Core/CallEvent.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index ecf1d1b5f0688..3785f498414f9 100644 --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -47,6 +47,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerIntPair.h" @@ -466,6 +467,42 @@ bool CallEvent::isVariadic(const Decl *D) { llvm_unreachable("unknown callable kind"); } +static bool isTransparentUnion(QualType T) { + const RecordType *UT = T->getAsUnionType(); + return UT && UT->getDecl()->hasAttr(); +} + +// In some cases, symbolic cases should be transformed before we associate +// them with parameters. This function incapsulates such cases. +static SVal processArgument(SVal Value, const Expr *ArgumentExpr, +const ParmVarDecl *Parameter, SValBuilder ) { + QualType ParamType = Parameter->getType(); + QualType ArgumentType = ArgumentExpr->getType(); + + // Transparent unions allow users to easily convert values of union field + // types into union-typed objects. + // + // Also, more importantly, they allow users to define functions with diff erent + // diff erent parameter types, substituting types matching transparent union + // field types with the union type itself. + // + // Here, we check specifically for latter cases and prevent binding + // field-typed values to union-typed regions. + if (isTransparentUnion(ParamType) && + // Let's check that we indeed trying to bind diff erent types. + !isTransparentUnion(ArgumentType)) { +BasicValueFactory = SVB.getBasicValueFactory(); + +llvm::ImmutableList CompoundSVals = BVF.getEmptySValList(); +CompoundSVals = BVF.prependSVal(Value, CompoundSVals); + +// Wrap it with compound value. +return SVB.makeCompoundVal(ParamType, CompoundSVals); + } + + return Value; +} + static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx, CallEvent::BindingsTy , SValBuilder , @@ -490,10 +527,12 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx, // determined in compile-time but not represented as arg-expressions, // which makes getArgSVal() fail and return UnknownVal. SVal ArgVal = Call.getArgSVal(Idx); +const Expr *ArgExpr = Call.getArgExpr(Idx); if (!ArgVal.isUnknown()) { Loc ParamLoc = SVB.makeLoc( MRMgr.getParamVarRegion(Call.getOriginExpr(), Idx, CalleeCtx)); - Bindings.push_back(std::make_pair(ParamLoc, ArgVal)); + Bindings.push_back( + std::make_pair(ParamLoc, processArgument(ArgVal, ArgExpr, *I, SVB))); } } diff --git a/clang/test/Analysis/transparent_union_bug.c b/clang/test/Analysis/transparent_union_bug.c new file mode 100644 index 0..b6069c6a59b19 --- /dev/null +++ b/clang/test/Analysis/transparent_union_bug.c @@ -0,0 +1,40 @@ +// RUN: %clang_analyze_cc1 -analyze -triple x86_64-apple-darwin10 \ +// RUN: -analyzer-checker=core,debug.ExprInspection -verify %s + +void clang_analyzer_warnIfReached(); + +typedef struct { + int value; +} Struct; + +typedef union { + Struct *ptr; + long num; +} __attribute__((transparent_union)) Alias; + +void foo(Struct *x); +void foo(Alias y) { + if (y.ptr == 0) { +// no-crash + } + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} +void foobar(long z); +void foobar(Alias z) { + if (z.num != 42) { +// no-crash + } + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void foobaz(Alias x) { + if (x.ptr == 0) { +// no-crash + } + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} +void bar(Struct arg) { + foo(); + foobar(42); + foobaz(); +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] cc2ef19 - [analyzer] Handle NTTP invocation in CallContext.getCalleeDecl()
Author: Tomasz Kamiński Date: 2021-06-18T16:32:19+03:00 New Revision: cc2ef195560999d0690a8d8805ea811270e38f26 URL: https://github.com/llvm/llvm-project/commit/cc2ef195560999d0690a8d8805ea811270e38f26 DIFF: https://github.com/llvm/llvm-project/commit/cc2ef195560999d0690a8d8805ea811270e38f26.diff LOG: [analyzer] Handle NTTP invocation in CallContext.getCalleeDecl() This fixes a crash in MallocChecker for the situation when operator new (delete) is invoked via NTTP and makes the behavior of CallContext.getCalleeDecl(Expr) identical to CallEvent.getDecl(). Reviewed By: vsavchenko Differential Revision: https://reviews.llvm.org/D103025 Added: Modified: clang/lib/StaticAnalyzer/Core/CheckerContext.cpp clang/test/Analysis/NewDelete-checker-test.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp b/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp index 3d44d2cbc069d..3d64ce453479f 100644 --- a/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -19,6 +19,10 @@ using namespace clang; using namespace ento; const FunctionDecl *CheckerContext::getCalleeDecl(const CallExpr *CE) const { + const FunctionDecl *D = CE->getDirectCallee(); + if (D) +return D; + const Expr *Callee = CE->getCallee(); SVal L = Pred->getSVal(Callee); return L.getAsFunctionDecl(); diff --git a/clang/test/Analysis/NewDelete-checker-test.cpp b/clang/test/Analysis/NewDelete-checker-test.cpp index 5a8711fa8a7ad..86df9d01dfb01 100644 --- a/clang/test/Analysis/NewDelete-checker-test.cpp +++ b/clang/test/Analysis/NewDelete-checker-test.cpp @@ -421,3 +421,36 @@ void shouldNotReportLeak() { Derived *p = (Derived *)allocate(); delete p; } + +template +void* allocate_via_nttp(size_t n) { + return allocate_fn(n); +} + +template +void deallocate_via_nttp(void* ptr) { + deallocate_fn(ptr); +} + +void testNTTPNewNTTPDelete() { + void* p = allocate_via_nttp<::operator new>(10); + deallocate_via_nttp<::operator delete>(p); +} // no warn + +void testNTTPNewDirectDelete() { + void* p = allocate_via_nttp<::operator new>(10); + ::operator delete(p); +} // no warn + +void testDirectNewNTTPDelete() { + void* p = ::operator new(10); + deallocate_via_nttp<::operator delete>(p); +} + +void not_free(void*) { +} + +void testLeakBecauseNTTPIsNotDeallocation() { + void* p = ::operator new(10); + deallocate_via_nttp(p); +} // leak-warning{{Potential leak of memory pointed to by 'p'}} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] eadd54f - [analyzer] Decouple NoteTag from its Factory
Author: Valeriy Savchenko Date: 2021-06-15T11:58:13+03:00 New Revision: eadd54f2741f9dc7307512382a7c8fb49aa840d0 URL: https://github.com/llvm/llvm-project/commit/eadd54f2741f9dc7307512382a7c8fb49aa840d0 DIFF: https://github.com/llvm/llvm-project/commit/eadd54f2741f9dc7307512382a7c8fb49aa840d0.diff LOG: [analyzer] Decouple NoteTag from its Factory This allows us to create other types of tags that carry useful bits of information alongside. Differential Revision: https://reviews.llvm.org/D104135 Added: Modified: clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h clang/lib/StaticAnalyzer/Core/CoreEngine.cpp Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h index 0b12ff7075780..99cd24a52f2df 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -725,14 +725,43 @@ class BugReporterContext { } }; +/// The tag that carries some information with it. +/// +/// It can be valuable to produce tags with some bits of information and later +/// reuse them for a better diagnostic. +/// +/// Please make sure that derived class' constuctor is private and that the user +/// can only create objects using DataTag::Factory. This also means that +/// DataTag::Factory should be friend for every derived class. +class DataTag : public ProgramPointTag { +public: + StringRef getTagDescription() const override { return "Data Tag"; } + + // Manage memory for DataTag objects. + class Factory { +std::vector> Tags; + + public: +template +const DataTagType *make(Args &&... ConstructorArgs) { + // We cannot use std::make_unique because we cannot access the private + // constructor from inside it. + Tags.emplace_back( + new DataTagType(std::forward(ConstructorArgs)...)); + return static_cast(Tags.back().get()); +} + }; + +protected: + DataTag(void *TagKind) : ProgramPointTag(TagKind) {} +}; /// The tag upon which the TagVisitor reacts. Add these in order to display /// additional PathDiagnosticEventPieces along the path. -class NoteTag : public ProgramPointTag { +class NoteTag : public DataTag { public: - using Callback = - std::function; + using Callback = std::function; private: static int Kind; @@ -741,7 +770,7 @@ class NoteTag : public ProgramPointTag { const bool IsPrunable; NoteTag(Callback &, bool IsPrunable) - : ProgramPointTag(), Cb(std::move(Cb)), IsPrunable(IsPrunable) {} + : DataTag(), Cb(std::move(Cb)), IsPrunable(IsPrunable) {} public: static bool classof(const ProgramPointTag *T) { @@ -766,20 +795,7 @@ class NoteTag : public ProgramPointTag { bool isPrunable() const { return IsPrunable; } - // Manage memory for NoteTag objects. - class Factory { -std::vector> Tags; - - public: -const NoteTag *makeNoteTag(Callback &, bool IsPrunable = false) { - // We cannot use std::make_unique because we cannot access the private - // constructor from inside it. - std::unique_ptr T(new NoteTag(std::move(Cb), IsPrunable)); - Tags.push_back(std::move(T)); - return Tags.back().get(); -} - }; - + friend class Factory; friend class TagVisitor; }; diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h index 54572bd81f20f..a383012dc3516 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -255,7 +255,7 @@ class CheckerContext { ///to omit the note from the report if it would make the displayed ///bug path significantly shorter. const NoteTag *getNoteTag(NoteTag::Callback &, bool IsPrunable = false) { -return Eng.getNoteTags().makeNoteTag(std::move(Cb), IsPrunable); +return Eng.getDataTags().make(std::move(Cb), IsPrunable); } /// A shorthand version of getNoteTag that doesn't require you to accept diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h index 2aca2c99ef4fd..9898b9b42f4b2 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -96,9 +96,10 @@ class CoreEngine { /// (This data is owned by AnalysisConsumer.) FunctionSummariesTy *FunctionSummaries; - /// Add path note tags along the
[clang] 16f7a95 - [analyzer] Simplify the process of producing notes for stores
Author: Valeriy Savchenko Date: 2021-06-15T11:37:36+03:00 New Revision: 16f7a952ec3e0f362690c6449951866100c6f76c URL: https://github.com/llvm/llvm-project/commit/16f7a952ec3e0f362690c6449951866100c6f76c DIFF: https://github.com/llvm/llvm-project/commit/16f7a952ec3e0f362690c6449951866100c6f76c.diff LOG: [analyzer] Simplify the process of producing notes for stores Differential Revision: https://reviews.llvm.org/D104046 Added: Modified: clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h index 8e39229ab1fd6..24cae12af24a1 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h @@ -330,6 +330,10 @@ class StoreHandler { TrackingOptions Opts) = 0; Tracker () { return ParentTracker; } + +protected: + PathDiagnosticPieceRef constructNote(StoreInfo SI, BugReporterContext , + StringRef NodeText); }; /// Visitor that tracks expressions and values. diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index dab1ba0a9d50f..d06a2d4933038 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1214,136 +1214,146 @@ static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) { return FrameSpace->getStackFrame() == LCtx->getStackFrame(); } +static bool isObjCPointer(const MemRegion *R) { + if (R->isBoundable()) +if (const auto *TR = dyn_cast(R)) + return TR->getValueType()->isObjCObjectPointerType(); + + return false; +} + +static bool isObjCPointer(const ValueDecl *D) { + return D->getType()->isObjCObjectPointerType(); +} + /// Show diagnostics for initializing or declaring a region \p R with a bad value. -static void showBRDiagnostics(const char *action, llvm::raw_svector_ostream , - const MemRegion *NewR, SVal V, - const MemRegion *OldR, const DeclStmt *DS) { - if (NewR->canPrintPretty()) { -NewR->printPretty(os); -os << " "; - } - - if (V.getAs()) { -bool b = false; -if (NewR->isBoundable()) { - if (const auto *TR = dyn_cast(NewR)) { -if (TR->getValueType()->isObjCObjectPointerType()) { - os << action << "nil"; - b = true; -} - } -} -if (!b) - os << action << "a null pointer value"; - - } else if (auto CVal = V.getAs()) { -os << action << CVal->getValue(); - } else if (OldR && OldR->canPrintPretty()) { -os << action << "the value of "; -OldR->printPretty(os); - } else if (DS) { -if (V.isUndef()) { - if (isa(NewR)) { +static void showBRDiagnostics(llvm::raw_svector_ostream , StoreInfo SI) { + const bool HasPrefix = SI.Dest->canPrintPretty(); + + if (HasPrefix) { +SI.Dest->printPretty(OS); +OS << " "; + } + + const char *Action = nullptr; + + switch (SI.StoreKind) { + case StoreInfo::Initialization: +Action = HasPrefix ? "initialized to " : "Initializing to "; +break; + case StoreInfo::BlockCapture: +Action = HasPrefix ? "captured by block as " : "Captured by block as "; +break; + default: +llvm_unreachable("Unexpected store kind"); + } + + if (SI.Value.getAs()) { +OS << Action << (isObjCPointer(SI.Dest) ? "nil" : "a null pointer value"); + + } else if (auto CVal = SI.Value.getAs()) { +OS << Action << CVal->getValue(); + + } else if (SI.Origin && SI.Origin->canPrintPretty()) { +OS << Action << "the value of "; +SI.Origin->printPretty(OS); + + } else if (SI.StoreKind == StoreInfo::Initialization) { +// We don't need to check here, all these conditions were +// checked by StoreSiteFinder, when it figured out that it is +// initialization. +const auto *DS = +cast(SI.StoreSite->getLocationAs()->getStmt()); + +if (SI.Value.isUndef()) { + if (isa(SI.Dest)) { const auto *VD = cast(DS->getSingleDecl()); + if (VD->getInit()) { - os << (NewR->canPrintPretty() ? "initialized" : "Initializing") + OS << (HasPrefix ? "initialized" : "Initializing") << " to a garbage value"; } else { - os << (NewR->canPrintPretty() ? "declared" : "Declaring") + OS << (HasPrefix ? "declared" : "Declaring") << " without an initial value"; } } } else { - os << (NewR->canPrintPretty() ? "initialized" : "Initialized") << " here"; + OS << (HasPrefix ?
[clang] 6e6a26b - [analyzer] Extract InlinedFunctionCallHandler
Author: Valeriy Savchenko Date: 2021-06-15T11:37:36+03:00 New Revision: 6e6a26b8f0ea8300d5a814e4150e225c33ec25de URL: https://github.com/llvm/llvm-project/commit/6e6a26b8f0ea8300d5a814e4150e225c33ec25de DIFF: https://github.com/llvm/llvm-project/commit/6e6a26b8f0ea8300d5a814e4150e225c33ec25de.diff LOG: [analyzer] Extract InlinedFunctionCallHandler Differential Revision: https://reviews.llvm.org/D103961 Added: Modified: clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 835c54ef5f68..dab1ba0a9d50 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -937,91 +937,6 @@ class ReturnVisitor : public TrackingBugReporterVisitor { ID.AddBoolean(EnableNullFPSuppression); } - /// Adds a ReturnVisitor if the given statement represents a call that was - /// inlined. - /// - /// This will search back through the ExplodedGraph, starting from the given - /// node, looking for when the given statement was processed. If it turns out - /// the statement is a call that was inlined, we add the visitor to the - /// bug report, so it can print a note later. - static void addVisitorIfNecessary(TrackerRef ParentTracker, -const ExplodedNode *Node, const Stmt *S, -PathSensitiveBugReport , -bool InEnableNullFPSuppression, -bugreporter::TrackingKind TKind) { -if (!CallEvent::isCallStmt(S)) - return; - -// First, find when we processed the statement. -// If we work with a 'CXXNewExpr' that is going to be purged away before -// its call take place. We would catch that purge in the last condition -// as a 'StmtPoint' so we have to bypass it. -const bool BypassCXXNewExprEval = isa(S); - -// This is moving forward when we enter into another context. -const StackFrameContext *CurrentSFC = Node->getStackFrame(); - -do { - // If that is satisfied we found our statement as an inlined call. - if (Optional CEE = Node->getLocationAs()) -if (CEE->getCalleeContext()->getCallSite() == S) - break; - - // Try to move forward to the end of the call-chain. - Node = Node->getFirstPred(); - if (!Node) -break; - - const StackFrameContext *PredSFC = Node->getStackFrame(); - - // If that is satisfied we found our statement. - // FIXME: This code currently bypasses the call site for the - //conservatively evaluated allocator. - if (!BypassCXXNewExprEval) -if (Optional SP = Node->getLocationAs()) - // See if we do not enter into another context. - if (SP->getStmt() == S && CurrentSFC == PredSFC) -break; - - CurrentSFC = PredSFC; -} while (Node->getStackFrame() == CurrentSFC); - -// Next, step over any post-statement checks. -while (Node && Node->getLocation().getAs()) - Node = Node->getFirstPred(); -if (!Node) - return; - -// Finally, see if we inlined the call. -Optional CEE = Node->getLocationAs(); -if (!CEE) - return; - -const StackFrameContext *CalleeContext = CEE->getCalleeContext(); -if (CalleeContext->getCallSite() != S) - return; - -// Check the return value. -ProgramStateRef State = Node->getState(); -SVal RetVal = Node->getSVal(S); - -// Handle cases where a reference is returned and then immediately used. -if (cast(S)->isGLValue()) - if (Optional LValue = RetVal.getAs()) -RetVal = State->getSVal(*LValue); - -// See if the return value is NULL. If so, suppress the report. -AnalyzerOptions = State->getAnalysisManager().options; - -bool EnableNullFPSuppression = false; -if (InEnableNullFPSuppression && Options.ShouldSuppressNullReturnPaths) - if (Optional RetLoc = RetVal.getAs()) -EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue(); - -BR.addVisitor(ParentTracker, CalleeContext, - EnableNullFPSuppression, Options, TKind); - } - PathDiagnosticPieceRef visitNodeInitial(const ExplodedNode *N, BugReporterContext , PathSensitiveBugReport ) { @@ -2228,6 +2143,96 @@ class InterestingLValueHandler final : public ExpressionHandler { } }; +/// Adds a ReturnVisitor if the given statement represents a call that was +/// inlined. +/// +/// This will search back through the ExplodedGraph, starting from the given +/// node, looking for when the given statement was processed. If it turns out +/// the statement is a call that was inlined, we add the visitor
[clang] 2e49067 - [analyzer] Extract InterestingLValueHandler
Author: Valeriy Savchenko Date: 2021-06-15T11:37:36+03:00 New Revision: 2e490676ea2eb419e7b2f54e1e2e537d5244ebbc URL: https://github.com/llvm/llvm-project/commit/2e490676ea2eb419e7b2f54e1e2e537d5244ebbc DIFF: https://github.com/llvm/llvm-project/commit/2e490676ea2eb419e7b2f54e1e2e537d5244ebbc.diff LOG: [analyzer] Extract InterestingLValueHandler Differential Revision: https://reviews.llvm.org/D103917 Added: Modified: clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 23f925e49b15..835c54ef5f68 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -2151,7 +2151,8 @@ class ArrayIndexHandler final : public ExpressionHandler { } }; -class DefaultExpressionHandler final : public ExpressionHandler { +// TODO: extract it into more handlers +class InterestingLValueHandler final : public ExpressionHandler { public: using ExpressionHandler::ExpressionHandler; @@ -2219,13 +2220,26 @@ class DefaultExpressionHandler final : public ExpressionHandler { // previously. Report.addVisitor(*DV, InputNode); - getParentTracker().track(V, R, Opts, SFC); - -return Result; } } +return Result; + } +}; + +class DefaultExpressionHandler final : public ExpressionHandler { +public: + using ExpressionHandler::ExpressionHandler; + + Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode, + const ExplodedNode *LVNode, + TrackingOptions Opts) override { +ProgramStateRef LVState = LVNode->getState(); +const StackFrameContext *SFC = LVNode->getStackFrame(); +PathSensitiveBugReport = getParentTracker().getReport(); +Tracker::Result Result; + // If the expression is not an "lvalue expression", we can still // track the constraints on its contents. SVal V = LVState->getSValAsScalarOrLoc(Inner, LVNode->getLocationContext()); @@ -2332,6 +2346,7 @@ Tracker::Tracker(PathSensitiveBugReport ) : Report(Report) { addLowPriorityHandler(); addLowPriorityHandler(); addLowPriorityHandler(); + addLowPriorityHandler(); addLowPriorityHandler(); addLowPriorityHandler(); // Default store handlers. ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 40cb73b - [analyzer] Extract ArrayIndexHandler
Author: Valeriy Savchenko Date: 2021-06-15T11:37:36+03:00 New Revision: 40cb73bd20735051eb8f2d43735097d2ff46f0a7 URL: https://github.com/llvm/llvm-project/commit/40cb73bd20735051eb8f2d43735097d2ff46f0a7 DIFF: https://github.com/llvm/llvm-project/commit/40cb73bd20735051eb8f2d43735097d2ff46f0a7.diff LOG: [analyzer] Extract ArrayIndexHandler One interesting problem was discovered here. When we do interrupt Tracker's track flow, we want to interrupt only it and not all the other flows recursively. Differential Revision: https://reviews.llvm.org/D103914 Added: Modified: clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 29768f79c5dd..23f925e49b15 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -2134,6 +2134,23 @@ class NilReceiverHandler final : public ExpressionHandler { } }; +class ArrayIndexHandler final : public ExpressionHandler { +public: + using ExpressionHandler::ExpressionHandler; + + Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode, + const ExplodedNode *LVNode, + TrackingOptions Opts) override { +// Track the index if this is an array subscript. +if (const auto *Arr = dyn_cast(Inner)) + return getParentTracker().track( + Arr->getIdx(), LVNode, + {Opts.Kind, /*EnableNullFPSuppression*/ false}); + +return {}; + } +}; + class DefaultExpressionHandler final : public ExpressionHandler { public: using ExpressionHandler::ExpressionHandler; @@ -2146,12 +2163,6 @@ class DefaultExpressionHandler final : public ExpressionHandler { PathSensitiveBugReport = getParentTracker().getReport(); Tracker::Result Result; -// Track the index if this is an array subscript. -if (const auto *Arr = dyn_cast(Inner)) - Result.combineWith(getParentTracker().track( - Arr->getIdx(), LVNode, - {Opts.Kind, /*EnableNullFPSuppression*/ false})); - // See if the expression we're interested refers to a variable. // If so, we can track both its contents and constraints on its value. if (ExplodedGraph::isInterestingLValueExpr(Inner)) { @@ -2320,6 +2331,7 @@ Tracker::Tracker(PathSensitiveBugReport ) : Report(Report) { // Default expression handlers. addLowPriorityHandler(); addLowPriorityHandler(); + addLowPriorityHandler(); addLowPriorityHandler(); addLowPriorityHandler(); // Default store handlers. @@ -2340,8 +2352,12 @@ Tracker::Result Tracker::track(const Expr *E, const ExplodedNode *N, // Iterate through the handlers in the order according to their priorities. for (ExpressionHandlerPtr : ExpressionHandlers) { CombinedResult.combineWith(Handler->handle(Inner, N, LVNode, Opts)); -if (CombinedResult.WasInterrupted) +if (CombinedResult.WasInterrupted) { + // There is no need to confuse our users here. + // We got interrupted, but our users don't need to know about it. + CombinedResult.WasInterrupted = false; break; +} } return CombinedResult; ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 1639dcb - [analyzer] Extract NilReceiverHandler
Author: Valeriy Savchenko Date: 2021-06-15T11:37:36+03:00 New Revision: 1639dcb2798469c4372e50bcb6b4cf36ecffd9ce URL: https://github.com/llvm/llvm-project/commit/1639dcb2798469c4372e50bcb6b4cf36ecffd9ce DIFF: https://github.com/llvm/llvm-project/commit/1639dcb2798469c4372e50bcb6b4cf36ecffd9ce.diff LOG: [analyzer] Extract NilReceiverHandler Differential Revision: https://reviews.llvm.org/D103902 Added: Modified: clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 10b674be4972..29768f79c5dd 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -2116,24 +2116,35 @@ class ControlDependencyHandler final : public ExpressionHandler { } }; -class DefaultExpressionHandler final : public ExpressionHandler { +class NilReceiverHandler final : public ExpressionHandler { public: using ExpressionHandler::ExpressionHandler; Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode, const ExplodedNode *LVNode, TrackingOptions Opts) override { -ProgramStateRef LVState = LVNode->getState(); -const StackFrameContext *SFC = LVNode->getStackFrame(); -PathSensitiveBugReport = getParentTracker().getReport(); -Tracker::Result Result; - // The message send could be nil due to the receiver being nil. // At this point in the path, the receiver should be live since we are at // the message send expr. If it is nil, start tracking it. if (const Expr *Receiver = NilReceiverBRVisitor::getNilReceiver(Inner, LVNode)) - Result.combineWith(getParentTracker().track(Receiver, LVNode, Opts)); + return getParentTracker().track(Receiver, LVNode, Opts); + +return {}; + } +}; + +class DefaultExpressionHandler final : public ExpressionHandler { +public: + using ExpressionHandler::ExpressionHandler; + + Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode, + const ExplodedNode *LVNode, + TrackingOptions Opts) override { +ProgramStateRef LVState = LVNode->getState(); +const StackFrameContext *SFC = LVNode->getStackFrame(); +PathSensitiveBugReport = getParentTracker().getReport(); +Tracker::Result Result; // Track the index if this is an array subscript. if (const auto *Arr = dyn_cast(Inner)) @@ -2308,6 +2319,7 @@ class PRValueHandler final : public ExpressionHandler { Tracker::Tracker(PathSensitiveBugReport ) : Report(Report) { // Default expression handlers. addLowPriorityHandler(); + addLowPriorityHandler(); addLowPriorityHandler(); addLowPriorityHandler(); // Default store handlers. ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 85f475c - [analyzer] Extract ControlDependencyHandler
Author: Valeriy Savchenko Date: 2021-06-15T11:37:36+03:00 New Revision: 85f475c979aa49b1b833c9e66af9cb35eafd02c7 URL: https://github.com/llvm/llvm-project/commit/85f475c979aa49b1b833c9e66af9cb35eafd02c7 DIFF: https://github.com/llvm/llvm-project/commit/85f475c979aa49b1b833c9e66af9cb35eafd02c7.diff LOG: [analyzer] Extract ControlDependencyHandler Differential Revision: https://reviews.llvm.org/D103677 Added: Modified: clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 0d0b34d7782a..10b674be4972 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -2088,17 +2088,14 @@ class DefaultStoreHandler final : public StoreHandler { } }; -class DefaultExpressionHandler final : public ExpressionHandler { +class ControlDependencyHandler final : public ExpressionHandler { public: using ExpressionHandler::ExpressionHandler; Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode, const ExplodedNode *LVNode, TrackingOptions Opts) override { -ProgramStateRef LVState = LVNode->getState(); -const StackFrameContext *SFC = LVNode->getStackFrame(); PathSensitiveBugReport = getParentTracker().getReport(); -Tracker::Result Result; // We only track expressions if we believe that they are important. Chances // are good that control dependencies to the tracking point are also @@ -2106,14 +2103,31 @@ class DefaultExpressionHandler final : public ExpressionHandler { // this point. // TODO: Shouldn't we track control dependencies of every bug location, // rather than only tracked expressions? -if (LVState->getAnalysisManager() +if (LVNode->getState() +->getAnalysisManager() .getAnalyzerOptions() .ShouldTrackConditions) { Report.addVisitor( (), InputNode); - Result.FoundSomethingToTrack = true; + return {/*FoundSomethingToTrack=*/true}; } +return {}; + } +}; + +class DefaultExpressionHandler final : public ExpressionHandler { +public: + using ExpressionHandler::ExpressionHandler; + + Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode, + const ExplodedNode *LVNode, + TrackingOptions Opts) override { +ProgramStateRef LVState = LVNode->getState(); +const StackFrameContext *SFC = LVNode->getStackFrame(); +PathSensitiveBugReport = getParentTracker().getReport(); +Tracker::Result Result; + // The message send could be nil due to the receiver being nil. // At this point in the path, the receiver should be live since we are at // the message send expr. If it is nil, start tracking it. @@ -2293,7 +2307,8 @@ class PRValueHandler final : public ExpressionHandler { Tracker::Tracker(PathSensitiveBugReport ) : Report(Report) { // Default expression handlers. - addHighPriorityHandler(); + addLowPriorityHandler(); + addLowPriorityHandler(); addLowPriorityHandler(); // Default store handlers. addHighPriorityHandler(); ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] bbebf38 - [analyzer] Refactor StoreSiteFinder and extract DefaultStoreHandler
Author: Valeriy Savchenko Date: 2021-06-15T11:37:35+03:00 New Revision: bbebf38b736a12c9582f9ae59c8e245a6ed68cb8 URL: https://github.com/llvm/llvm-project/commit/bbebf38b736a12c9582f9ae59c8e245a6ed68cb8 DIFF: https://github.com/llvm/llvm-project/commit/bbebf38b736a12c9582f9ae59c8e245a6ed68cb8.diff LOG: [analyzer] Refactor StoreSiteFinder and extract DefaultStoreHandler After this patch, custom StoreHandlers will also work as expected. Differential Revision: https://reviews.llvm.org/D103644 Added: Modified: clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h index 047b2072bbcd9..8e39229ab1fd6 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h @@ -125,6 +125,9 @@ struct StoreInfo { /// int x; /// x = 42; Assignment, +/// The value got stored into the parameter region as the result +/// of a call. +CallArgument, /// The value got stored into the region as block capture. /// Block data is modeled as a separate region, thus whenever /// the analyzer sees a captured variable, its value is copied @@ -138,7 +141,7 @@ struct StoreInfo { const ExplodedNode *StoreSite; /// The expression where the value comes from. /// NOTE: might be null. - Expr *SourceOfTheValue; + const Expr *SourceOfTheValue; /// Symbolic value that is being stored. SVal Value; /// Memory regions involved in the store operation. @@ -230,7 +233,8 @@ class Tracker : public llvm::RefCountedBase { /// \param Opts Tracking options specifying how we got to it. /// /// NOTE: this method is designed for sub-trackers and visitors. - virtual PathDiagnosticPieceRef handle(StoreInfo SI, TrackingOptions Opts); + virtual PathDiagnosticPieceRef handle(StoreInfo SI, BugReporterContext , +TrackingOptions Opts); /// Add custom expression handler with the highest priority. /// @@ -322,7 +326,8 @@ class StoreHandler { /// /// \return the produced note, null if the handler doesn't support this kind /// of stores. - virtual PathDiagnosticPieceRef handle(StoreInfo SI, TrackingOptions Opts) = 0; + virtual PathDiagnosticPieceRef handle(StoreInfo SI, BugReporterContext , +TrackingOptions Opts) = 0; Tracker () { return ParentTracker; } }; diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index c51b7db2b2f3c..0d0b34d7782a0 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1232,12 +1232,7 @@ class StoreSiteFinder final : public TrackingBugReporterVisitor { SVal V; bool Satisfied = false; - /// If the visitor is tracking the value directly responsible for the - /// bug, we are going to employ false positive suppression. - bool EnableNullFPSuppression; - - using TrackingKind = bugreporter::TrackingKind; - TrackingKind TKind; + TrackingOptions Options; const StackFrameContext *OriginSFC; public: @@ -1252,11 +1247,9 @@ class StoreSiteFinder final : public TrackingBugReporterVisitor { ///this visitor can prevent that without polluting the bugpath too ///much. StoreSiteFinder(bugreporter::TrackerRef ParentTracker, KnownSVal V, - const MemRegion *R, bool InEnableNullFPSuppression, - TrackingKind TKind, + const MemRegion *R, TrackingOptions Options, const StackFrameContext *OriginSFC = nullptr) - : TrackingBugReporterVisitor(ParentTracker), R(R), V(V), -EnableNullFPSuppression(InEnableNullFPSuppression), TKind(TKind), + : TrackingBugReporterVisitor(ParentTracker), R(R), V(V), Options(Options), OriginSFC(OriginSFC) { assert(R); } @@ -1273,8 +1266,8 @@ void StoreSiteFinder::Profile(llvm::FoldingSetNodeID ) const { ID.AddPointer(); ID.AddPointer(R); ID.Add(V); - ID.AddInteger(static_cast(TKind)); - ID.AddBoolean(EnableNullFPSuppression); + ID.AddInteger(static_cast(Options.Kind)); + ID.AddBoolean(Options.EnableNullFPSuppression); } /// Returns true if \p N represents the DeclStmt declaring and initializing @@ -1533,8 +1526,7 @@ PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ, if (!IsParam) InitE = InitE->IgnoreParenCasts(); -getParentTracker().track(InitE, StoreSite, - {TKind, EnableNullFPSuppression}); +
[clang] 57006d2 - [analyzer] Refactor trackExpressionValue to accept TrackingOptions
Author: Valeriy Savchenko Date: 2021-06-11T12:49:04+03:00 New Revision: 57006d2f6d96d8a6836ae901218ed615071b3b8e URL: https://github.com/llvm/llvm-project/commit/57006d2f6d96d8a6836ae901218ed615071b3b8e DIFF: https://github.com/llvm/llvm-project/commit/57006d2f6d96d8a6836ae901218ed615071b3b8e.diff LOG: [analyzer] Refactor trackExpressionValue to accept TrackingOptions Differential Revision: https://reviews.llvm.org/D103633 Added: Modified: clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h index ba652be33fb5..17a8e7859a54 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h @@ -353,9 +353,7 @@ class TrackingBugReporterVisitor : public BugReporterVisitor { /// statement. Note that returning \c true does not actually imply /// that any visitors were added. bool trackExpressionValue(const ExplodedNode *N, const Expr *E, - PathSensitiveBugReport , - TrackingKind TKind = TrackingKind::Thorough, - bool EnableNullFPSuppression = true); + PathSensitiveBugReport , TrackingOptions Opts = {}); /// Track how the value got stored into the given region and where it came /// from. diff --git a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp index 837213875a60..b72d72580c28 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp @@ -284,8 +284,9 @@ void MIGChecker::checkReturnAux(const ReturnStmt *RS, CheckerContext ) const { N); R->addRange(RS->getSourceRange()); - bugreporter::trackExpressionValue(N, RS->getRetValue(), *R, -bugreporter::TrackingKind::Thorough, false); + bugreporter::trackExpressionValue( + N, RS->getRetValue(), *R, + {bugreporter::TrackingKind::Thorough, /*EnableNullFPSuppression=*/false}); C.emitReport(std::move(R)); } diff --git a/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp index 8c2008a7ceb4..13985af76b00 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp @@ -147,8 +147,9 @@ void ObjCContainersChecker::checkPreStmt(const CallExpr *CE, auto R = std::make_unique( *BT, "Index is out of bounds", N); R->addRange(IdxExpr->getSourceRange()); - bugreporter::trackExpressionValue( - N, IdxExpr, *R, bugreporter::TrackingKind::Thorough, false); + bugreporter::trackExpressionValue(N, IdxExpr, *R, +{bugreporter::TrackingKind::Thorough, + /*EnableNullFPSuppression=*/false}); C.emitReport(std::move(R)); return; } diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index ede76154ac7a..c51b7db2b2f3 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -2314,11 +2314,11 @@ PathDiagnosticPieceRef Tracker::handle(StoreInfo SI, TrackingOptions Opts) { bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, const Expr *E, - PathSensitiveBugReport , - bugreporter::TrackingKind TKind, - bool EnableNullFPSuppression) { - return Tracker::create(report) - ->track(E, InputNode, {TKind, EnableNullFPSuppression}) + + PathSensitiveBugReport , + TrackingOptions Opts) { + return Tracker::create(Report) + ->track(E, InputNode, Opts) .FoundSomethingToTrack; } @@ -2375,9 +2375,9 @@ NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext , // The receiver was nil, and hence the method was skipped. // Register a BugReporterVisitor to issue a message telling us how // the receiver was null. - bugreporter::trackExpressionValue( - N, Receiver, BR, bugreporter::TrackingKind::Thorough, - /*EnableNullFPSuppression*/ false); + bugreporter::trackExpressionValue(N, Receiver, BR, +
[clang] 51d4704 - [analyzer] Turn TrackControlDependencyCond into a tracking visitor
Author: Valeriy Savchenko Date: 2021-06-11T12:49:04+03:00 New Revision: 51d4704d5ec9b8e4e5e445ee69c56a58250e370e URL: https://github.com/llvm/llvm-project/commit/51d4704d5ec9b8e4e5e445ee69c56a58250e370e DIFF: https://github.com/llvm/llvm-project/commit/51d4704d5ec9b8e4e5e445ee69c56a58250e370e.diff LOG: [analyzer] Turn TrackControlDependencyCond into a tracking visitor Differential Revision: https://reviews.llvm.org/D103631 Added: Modified: clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 677a1d39d8a0..ede76154ac7a 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1855,14 +1855,17 @@ namespace { /// An error is emitted at line 3. This visitor realizes that the branch /// on line 2 is a control dependency of line 3, and tracks it's condition via /// trackExpressionValue(). -class TrackControlDependencyCondBRVisitor final : public BugReporterVisitor { +class TrackControlDependencyCondBRVisitor final +: public TrackingBugReporterVisitor { const ExplodedNode *Origin; ControlDependencyCalculator ControlDeps; llvm::SmallSet VisitedBlocks; public: - TrackControlDependencyCondBRVisitor(const ExplodedNode *O) - : Origin(O), ControlDeps(>getCFG()) {} + TrackControlDependencyCondBRVisitor(TrackerRef ParentTracker, + const ExplodedNode *O) + : TrackingBugReporterVisitor(ParentTracker), Origin(O), +ControlDeps(>getCFG()) {} void Profile(llvm::FoldingSetNodeID ) const override { static int x = 0; @@ -1960,9 +1963,9 @@ TrackControlDependencyCondBRVisitor::VisitNode(const ExplodedNode *N, // isn't sufficient, because a new visitor is created for each tracked // expression, hence the BugReport level set. if (BR.addTrackedCondition(N)) { -bugreporter::trackExpressionValue( -N, Condition, BR, bugreporter::TrackingKind::Condition, -/*EnableNullFPSuppression=*/false); +getParentTracker().track(Condition, N, + {bugreporter::TrackingKind::Condition, + /*EnableNullFPSuppression=*/false}); return constructDebugPieceForTrackedCondition(Condition, N, BRC); } } @@ -2078,7 +2081,8 @@ class DefaultExpressionHandler final : public ExpressionHandler { if (LVState->getAnalysisManager() .getAnalyzerOptions() .ShouldTrackConditions) { - Report.addVisitor(InputNode); + Report.addVisitor( + (), InputNode); Result.FoundSomethingToTrack = true; } ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 3fc8d94 - [analyzer] Refactor trackRValueExpression into ExpressionHandler
Author: Valeriy Savchenko Date: 2021-06-11T12:49:04+03:00 New Revision: 3fc8d943c360f801a428ff24569d2dd53a2afe0f URL: https://github.com/llvm/llvm-project/commit/3fc8d943c360f801a428ff24569d2dd53a2afe0f DIFF: https://github.com/llvm/llvm-project/commit/3fc8d943c360f801a428ff24569d2dd53a2afe0f.diff LOG: [analyzer] Refactor trackRValueExpression into ExpressionHandler Differential Revision: https://reviews.llvm.org/D103630 Added: Modified: clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 651c80276110..677a1d39d8a0 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -2053,44 +2053,6 @@ static const ExplodedNode* findNodeForExpression(const ExplodedNode *N, return N; } -/// Attempts to add visitors to track an RValue expression back to its point of -/// origin. Works similarly to trackExpressionValue, but accepts only RValues. -static void trackRValueExpression(const ExplodedNode *InputNode, const Expr *E, - PathSensitiveBugReport , - bugreporter::TrackingKind TKind, - bool EnableNullFPSuppression) { - assert(E->isPRValue() && "The expression is not a prvalue!"); - const ExplodedNode *RVNode = findNodeForExpression(InputNode, E); - if (!RVNode) -return; - ProgramStateRef RVState = RVNode->getState(); - SVal V = RVState->getSValAsScalarOrLoc(E, RVNode->getLocationContext()); - const auto *BO = dyn_cast(E); - if (!BO) -return; - if (!V.isZeroConstant()) -return; - if (!BO->isMultiplicativeOp()) -return; - - SVal RHSV = RVState->getSVal(BO->getRHS(), RVNode->getLocationContext()); - SVal LHSV = RVState->getSVal(BO->getLHS(), RVNode->getLocationContext()); - - // Track both LHS and RHS of a multiplication. - if (BO->getOpcode() == BO_Mul) { -if (LHSV.isZeroConstant()) - trackExpressionValue(InputNode, BO->getLHS(), report, TKind, - EnableNullFPSuppression); -if (RHSV.isZeroConstant()) - trackExpressionValue(InputNode, BO->getRHS(), report, TKind, - EnableNullFPSuppression); - } else { // Track only the LHS of a division or a modulo. -if (LHSV.isZeroConstant()) - trackExpressionValue(InputNode, BO->getLHS(), report, TKind, - EnableNullFPSuppression); - } -} - //===--===// //Tracker implementation //===--===// @@ -2245,17 +2207,61 @@ class DefaultExpressionHandler final : public ExpressionHandler { } } -if (Inner->isPRValue()) - // TODO: Incorporate as Handler - trackRValueExpression(LVNode, Inner, Report, Opts.Kind, -Opts.EnableNullFPSuppression); - return Result; } }; +/// Attempts to add visitors to track an RValue expression back to its point of +/// origin. +class PRValueHandler final : public ExpressionHandler { +public: + using ExpressionHandler::ExpressionHandler; + + Tracker::Result handle(const Expr *E, const ExplodedNode *InputNode, + const ExplodedNode *ExprNode, + TrackingOptions Opts) override { +if (!E->isPRValue()) + return {}; + +const ExplodedNode *RVNode = findNodeForExpression(ExprNode, E); +if (!RVNode) + return {}; + +ProgramStateRef RVState = RVNode->getState(); +SVal V = RVState->getSValAsScalarOrLoc(E, RVNode->getLocationContext()); +const auto *BO = dyn_cast(E); + +if (!BO || !BO->isMultiplicativeOp() || !V.isZeroConstant()) + return {}; + +SVal RHSV = RVState->getSVal(BO->getRHS(), RVNode->getLocationContext()); +SVal LHSV = RVState->getSVal(BO->getLHS(), RVNode->getLocationContext()); + +// Track both LHS and RHS of a multiplication. +Tracker::Result CombinedResult; +Tracker = getParentTracker(); + +const auto track = [, , ExprNode, Opts](Expr *Inner) { + CombinedResult.combineWith(Parent.track(Inner, ExprNode, Opts)); +}; + +if (BO->getOpcode() == BO_Mul) { + if (LHSV.isZeroConstant()) +track(BO->getLHS()); + if (RHSV.isZeroConstant()) +track(BO->getRHS()); +} else { // Track only the LHS of a division or a modulo. + if (LHSV.isZeroConstant()) +track(BO->getLHS()); +} + +return CombinedResult; + } +}; + Tracker::Tracker(PathSensitiveBugReport ) : Report(Report) { addHighPriorityHandler(); + addLowPriorityHandler(); // TODO: split trackExpressionValue and FindLastStoreBRVisitor into handlers //
[clang] f853d26 - [analyzer] Turn ReturnVisitor into a tracking visitor
Author: Valeriy Savchenko Date: 2021-06-11T12:49:03+03:00 New Revision: f853d2601abd4f6ab789ca1513ae8b59ba5d38b7 URL: https://github.com/llvm/llvm-project/commit/f853d2601abd4f6ab789ca1513ae8b59ba5d38b7 DIFF: https://github.com/llvm/llvm-project/commit/f853d2601abd4f6ab789ca1513ae8b59ba5d38b7.diff LOG: [analyzer] Turn ReturnVisitor into a tracking visitor Whenever Tracker spawns a visitor that needs to call tracker back, we have to use TrackingBugReporterVisitor in order to maintain all the hooks that the checker might've used. Differential Revision: https://reviews.llvm.org/D103628 Added: Modified: clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index af553a2b5903..651c80276110 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -906,7 +906,7 @@ namespace { /// /// This visitor is intended to be used when another visitor discovers that an /// interesting value comes from an inlined function call. -class ReturnVisitor : public BugReporterVisitor { +class ReturnVisitor : public TrackingBugReporterVisitor { const StackFrameContext *CalleeSFC; enum { Initial, @@ -920,10 +920,11 @@ class ReturnVisitor : public BugReporterVisitor { bugreporter::TrackingKind TKind; public: - ReturnVisitor(const StackFrameContext *Frame, bool Suppressed, -AnalyzerOptions , bugreporter::TrackingKind TKind) - : CalleeSFC(Frame), EnableNullFPSuppression(Suppressed), -Options(Options), TKind(TKind) {} + ReturnVisitor(TrackerRef ParentTracker, const StackFrameContext *Frame, +bool Suppressed, AnalyzerOptions , +bugreporter::TrackingKind TKind) + : TrackingBugReporterVisitor(ParentTracker), CalleeSFC(Frame), +EnableNullFPSuppression(Suppressed), Options(Options), TKind(TKind) {} static void *getTag() { static int Tag = 0; @@ -943,7 +944,8 @@ class ReturnVisitor : public BugReporterVisitor { /// node, looking for when the given statement was processed. If it turns out /// the statement is a call that was inlined, we add the visitor to the /// bug report, so it can print a note later. - static void addVisitorIfNecessary(const ExplodedNode *Node, const Stmt *S, + static void addVisitorIfNecessary(TrackerRef ParentTracker, +const ExplodedNode *Node, const Stmt *S, PathSensitiveBugReport , bool InEnableNullFPSuppression, bugreporter::TrackingKind TKind) { @@ -1016,8 +1018,8 @@ class ReturnVisitor : public BugReporterVisitor { if (Optional RetLoc = RetVal.getAs()) EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue(); -BR.addVisitor(CalleeContext, EnableNullFPSuppression, - Options, TKind); +BR.addVisitor(ParentTracker, CalleeContext, + EnableNullFPSuppression, Options, TKind); } PathDiagnosticPieceRef visitNodeInitial(const ExplodedNode *N, @@ -1066,8 +1068,7 @@ class ReturnVisitor : public BugReporterVisitor { RetE = RetE->IgnoreParenCasts(); // Let's track the return value. -bugreporter::trackExpressionValue( -N, RetE, BR, TKind, EnableNullFPSuppression); +getParentTracker().track(RetE, N, {TKind, EnableNullFPSuppression}); // Build an appropriate message based on the return value. SmallString<64> Msg; @@ -1183,7 +1184,9 @@ class ReturnVisitor : public BugReporterVisitor { if (!State->isNull(*ArgV).isConstrainedTrue()) continue; - if (trackExpressionValue(N, ArgE, BR, TKind, EnableNullFPSuppression)) + if (getParentTracker() + .track(ArgE, N, {TKind, EnableNullFPSuppression}) + .FoundSomethingToTrack) ShouldInvalidate = false; // If we /can't/ track the null pointer, we should err on the side of @@ -2197,8 +2200,9 @@ class DefaultExpressionHandler final : public ExpressionHandler { // track the constraints on its contents. SVal V = LVState->getSValAsScalarOrLoc(Inner, LVNode->getLocationContext()); -ReturnVisitor::addVisitorIfNecessary( -LVNode, Inner, Report, Opts.EnableNullFPSuppression, Opts.Kind); +ReturnVisitor::addVisitorIfNecessary((), LVNode, Inner, + Report, Opts.EnableNullFPSuppression, + Opts.Kind); // Is it a symbolic value? if (auto L = V.getAs()) { ___ cfe-commits mailing list cfe-commits@lists.llvm.org
[clang] 87a5c4d - [analyzer] Hide and rename FindLastStoreBRVisitor
Author: Valeriy Savchenko Date: 2021-06-11T12:49:03+03:00 New Revision: 87a5c4d3745a06ec0594fa3f7aaf7f58a53315ec URL: https://github.com/llvm/llvm-project/commit/87a5c4d3745a06ec0594fa3f7aaf7f58a53315ec DIFF: https://github.com/llvm/llvm-project/commit/87a5c4d3745a06ec0594fa3f7aaf7f58a53315ec.diff LOG: [analyzer] Hide and rename FindLastStoreBRVisitor This component should not be used directly at this point and it is simply an implementation detail, that's why StoreSiteFinder is out of the header file. Differential Revision: https://reviews.llvm.org/D103624 Added: Modified: clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h index 4b0d5378e0c13..ba652be33fb52 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h @@ -378,50 +378,6 @@ const Expr *getDerefExpr(const Stmt *S); } // namespace bugreporter -/// Finds last store into the given region, -/// which is diff erent from a given symbolic value. -class FindLastStoreBRVisitor final -: public bugreporter::TrackingBugReporterVisitor { - const MemRegion *R; - SVal V; - bool Satisfied = false; - - /// If the visitor is tracking the value directly responsible for the - /// bug, we are going to employ false positive suppression. - 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 - /// diff erent 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(bugreporter::TrackerRef ParentTracker, KnownSVal V, - const MemRegion *R, bool InEnableNullFPSuppression, - TrackingKind TKind, - const StackFrameContext *OriginSFC = nullptr) - : TrackingBugReporterVisitor(ParentTracker), R(R), V(V), -EnableNullFPSuppression(InEnableNullFPSuppression), TKind(TKind), -OriginSFC(OriginSFC) { -assert(R); - } - - void Profile(llvm::FoldingSetNodeID ) const override; - - PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, - BugReporterContext , - PathSensitiveBugReport ) override; -}; - class TrackConstraintBRVisitor final : public BugReporterVisitor { DefinedSVal Constraint; bool Assumption; diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index ec16a4deae029..af553a2b5903e 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1219,10 +1219,53 @@ class ReturnVisitor : public BugReporterVisitor { } // end of anonymous namespace //===--===// -// Implementation of FindLastStoreBRVisitor. +// StoreSiteFinder //===--===// -void FindLastStoreBRVisitor::Profile(llvm::FoldingSetNodeID ) const { +/// Finds last store into the given region, +/// which is diff erent from a given symbolic value. +class StoreSiteFinder final : public TrackingBugReporterVisitor { + const MemRegion *R; + SVal V; + bool Satisfied = false; + + /// If the visitor is tracking the value directly responsible for the + /// bug, we are going to employ false positive suppression. + 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 + /// diff erent stackframe to this one. Disregarded if the tracking kind + ///is thorough. + ///This is useful, because for
[clang] b6bcf95 - [analyzer] Change FindLastStoreBRVisitor to use Tracker
Author: Valeriy Savchenko Date: 2021-06-11T12:49:03+03:00 New Revision: b6bcf953220db7880f2bb508f6f5c02b41078b2c URL: https://github.com/llvm/llvm-project/commit/b6bcf953220db7880f2bb508f6f5c02b41078b2c DIFF: https://github.com/llvm/llvm-project/commit/b6bcf953220db7880f2bb508f6f5c02b41078b2c.diff LOG: [analyzer] Change FindLastStoreBRVisitor to use Tracker Additionally, this commit completely removes any uses of FindLastStoreBRVisitor from the analyzer except for the one in Tracker. The next step is actually removing this class altogether from the header file. Differential Revision: https://reviews.llvm.org/D103618 Added: Modified: clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h index 5c4345a32c8d3..4b0d5378e0c13 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h @@ -19,6 +19,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include @@ -147,6 +148,9 @@ struct StoreInfo { const MemRegion *Dest, *Origin; }; +class Tracker; +using TrackerRef = llvm::IntrusiveRefCntPtr; + class ExpressionHandler; class StoreHandler; @@ -155,7 +159,7 @@ class StoreHandler; /// Tracker aimes at providing a sensible set of default behaviors that can be /// used by any checker, while providing mechanisms to hook into any part of the /// tracking process and insert checker-specific logic. -class Tracker { +class Tracker : public llvm::RefCountedBase { private: using ExpressionHandlerPtr = std::unique_ptr; using StoreHandlerPtr = std::unique_ptr; @@ -164,11 +168,17 @@ class Tracker { std::list ExpressionHandlers; std::list StoreHandlers; -public: +protected: /// \param Report The bug report to which visitors should be attached. Tracker(PathSensitiveBugReport ); + +public: virtual ~Tracker() = default; + static TrackerRef create(PathSensitiveBugReport ) { +return new Tracker(Report); + } + PathSensitiveBugReport () { return Report; } /// Describes a tracking result with the most basic information of what was @@ -318,6 +328,18 @@ class StoreHandler { Tracker () { return ParentTracker; } }; +/// Visitor that tracks expressions and values. +class TrackingBugReporterVisitor : public BugReporterVisitor { +private: + TrackerRef ParentTracker; + +public: + TrackingBugReporterVisitor(TrackerRef ParentTracker) + : ParentTracker(ParentTracker) {} + + Tracker () { return *ParentTracker; } +}; + /// Attempts to add visitors to track expression value back to its point of /// origin. /// @@ -335,13 +357,31 @@ bool trackExpressionValue(const ExplodedNode *N, const Expr *E, TrackingKind TKind = TrackingKind::Thorough, bool EnableNullFPSuppression = true); +/// Track how the value got stored into the given region and where it came +/// from. +/// +/// \param V We're searching for the store where \c R received this value. +/// \param R The region we're tracking. +/// \param Opts Tracking options specifying how we want to track the value. +/// \param Origin Only adds notes when the last store happened in a +/// diff erent 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. +void trackStoredValue(KnownSVal V, const MemRegion *R, + PathSensitiveBugReport , TrackingOptions Opts = {}, + const StackFrameContext *Origin = nullptr); + const Expr *getDerefExpr(const Stmt *S); } // namespace bugreporter /// Finds last store into the given region, /// which is diff erent from a given symbolic value. -class FindLastStoreBRVisitor final : public BugReporterVisitor { +class FindLastStoreBRVisitor final +: public bugreporter::TrackingBugReporterVisitor { const MemRegion *R; SVal V; bool Satisfied = false; @@ -365,11 +405,13 @@ class FindLastStoreBRVisitor final : public BugReporterVisitor { ///changes to its value in a
[clang] 967c06b - [analyzer] Reimplement trackExpressionValue as ExpressionHandler
Author: Valeriy Savchenko Date: 2021-06-11T12:49:03+03:00 New Revision: 967c06b3e95ba776fb06ad0ea5aa699cf2e1b59a URL: https://github.com/llvm/llvm-project/commit/967c06b3e95ba776fb06ad0ea5aa699cf2e1b59a DIFF: https://github.com/llvm/llvm-project/commit/967c06b3e95ba776fb06ad0ea5aa699cf2e1b59a.diff LOG: [analyzer] Reimplement trackExpressionValue as ExpressionHandler This commit moves trackExpressionValue into the Tracker interface as DefaultExpressionHandler. It still can be split into smaller handlers, but that can be a future change. Additionally, this commit doesn't remove the original trackExpressionValue interface, so it's not too big. One of the next commits will address it. Differential Revision: https://reviews.llvm.org/D103616 Added: Modified: clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 9359a5fe80f1..2df3687f2bb4 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -2050,7 +2050,166 @@ static void trackRValueExpression(const ExplodedNode *InputNode, const Expr *E, //Tracker implementation //===--===// +class DefaultExpressionHandler final : public ExpressionHandler { +public: + using ExpressionHandler::ExpressionHandler; + + Tracker::Result handle(const Expr *Inner, const ExplodedNode *InputNode, + const ExplodedNode *LVNode, + TrackingOptions Opts) override { +ProgramStateRef LVState = LVNode->getState(); +const StackFrameContext *SFC = LVNode->getStackFrame(); +PathSensitiveBugReport = getParentTracker().getReport(); +Tracker::Result Result; + +// We only track expressions if we believe that they are important. Chances +// are good that control dependencies to the tracking point are also +// important because of this, let's explain why we believe control reached +// this point. +// TODO: Shouldn't we track control dependencies of every bug location, +// rather than only tracked expressions? +if (LVState->getAnalysisManager() +.getAnalyzerOptions() +.ShouldTrackConditions) { + Report.addVisitor(InputNode); + Result.FoundSomethingToTrack = true; +} + +// The message send could be nil due to the receiver being nil. +// At this point in the path, the receiver should be live since we are at +// the message send expr. If it is nil, start tracking it. +if (const Expr *Receiver = +NilReceiverBRVisitor::getNilReceiver(Inner, LVNode)) + Result.combineWith(getParentTracker().track(Receiver, LVNode, Opts)); + +// Track the index if this is an array subscript. +if (const auto *Arr = dyn_cast(Inner)) + Result.combineWith(getParentTracker().track( + Arr->getIdx(), LVNode, + {Opts.Kind, /*EnableNullFPSuppression*/ false})); + +// See if the expression we're interested refers to a variable. +// If so, we can track both its contents and constraints on its value. +if (ExplodedGraph::isInterestingLValueExpr(Inner)) { + SVal LVal = LVNode->getSVal(Inner); + + const MemRegion *RR = getLocationRegionIfReference(Inner, LVNode); + bool LVIsNull = LVState->isNull(LVal).isConstrainedTrue(); + + // If this is a C++ reference to a null pointer, we are tracking the + // pointer. In addition, we should find the store at which the reference + // got initialized. + if (RR && !LVIsNull) +Result.combineWith(getParentTracker().track(LVal, RR, Opts, SFC)); + + // In case of C++ references, we want to diff erentiate between a null + // reference and reference to null pointer. + // If the LVal is null, check if we are dealing with null reference. + // For those, we want to track the location of the reference. + const MemRegion *R = + (RR && LVIsNull) ? RR : LVNode->getSVal(Inner).getAsRegion(); + + if (R) { + +// Mark both the variable region and its contents as interesting. +SVal V = LVState->getRawSVal(loc::MemRegionVal(R)); +Report.addVisitor(cast(R), Opts.Kind); + +// When we got here, we do have something to track, and we will +// interrupt. +Result.FoundSomethingToTrack = true; +Result.WasInterrupted = true; + +MacroNullReturnSuppressionVisitor::addMacroVisitorIfNecessary( +LVNode, R, Opts.EnableNullFPSuppression, Report, V); + +Report.markInteresting(V, Opts.Kind); +Report.addVisitor(R); + +// If the contents are symbolic and null, find out when they became +// null. +if
[clang] 0cc3100 - [analyzer] Introduce a new interface for tracking
Author: Valeriy Savchenko Date: 2021-06-11T12:49:03+03:00 New Revision: 0cc3100bf8d126ce080c0075cf25784b45e5f990 URL: https://github.com/llvm/llvm-project/commit/0cc3100bf8d126ce080c0075cf25784b45e5f990 DIFF: https://github.com/llvm/llvm-project/commit/0cc3100bf8d126ce080c0075cf25784b45e5f990.diff LOG: [analyzer] Introduce a new interface for tracking Tracking values through expressions and the stores is fundamental for producing clear diagnostics. However, the main components participating in this process, namely `trackExpressionValue` and `FindLastStoreBRVisitor`, became pretty bloated. They have an interesting dynamic between them (and some other visitors) that one might call a "chain reaction". `trackExpressionValue` adds `FindLastStoreBRVisitor`, and the latter calls `trackExpressionValue`. Because of this design, individual checkers couldn't affect what's going to happen somewhere in the middle of that chain. Whether they want to produce a more informative note or keep the overall tracking going by utilizing some of the domain expertise. This all lead to two biggest problems that I see: * Some checkers don't use it This should probably never be the case for path-sensitive checks. * Some checkers incorporated their logic directly into those components This doesn't make the maintenance easier, breaks multiple architecture principles, and makes the code harder to read adn understand, thus, increasing the probability of the first case. This commit introduces a prototype for a new interface that will be responsible for tracking. My main idea here was to make operations that I want have as a checker developer easy to implement and hook directly into the tracking process. Differential Revision: https://reviews.llvm.org/D103605 Added: Modified: clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h index 2975d50de3334..5c4345a32c8d3 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h @@ -21,7 +21,9 @@ #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" +#include #include +#include namespace clang { @@ -99,6 +101,223 @@ enum class TrackingKind { Condition }; +/// Defines a set of options altering tracking behavior. +struct TrackingOptions { + /// Specifies the kind of tracking. + TrackingKind Kind = TrackingKind::Thorough; + /// Specifies whether we should employ false positive suppression + /// (inlined defensive checks, returned null). + bool EnableNullFPSuppression = true; +}; + +/// Describes an event when the value got stored into a memory region. +/// +/// As opposed to checker checkBind API, it reacts also to binds +/// generated by the checker as well. It can be useful when the binding +/// happened as a result of evalCall, for example. +struct StoreInfo { + enum Kind { +/// The value got stored into the region during initialization: +/// int x = 42; +Initialization, +/// The value got stored into the region during assignment: +/// int x; +/// x = 42; +Assignment, +/// The value got stored into the region as block capture. +/// Block data is modeled as a separate region, thus whenever +/// the analyzer sees a captured variable, its value is copied +/// into a special block region. +BlockCapture + }; + + /// The type of store operation. + Kind StoreKind; + /// The node where the store happened. + const ExplodedNode *StoreSite; + /// The expression where the value comes from. + /// NOTE: might be null. + Expr *SourceOfTheValue; + /// Symbolic value that is being stored. + SVal Value; + /// Memory regions involved in the store operation. + /// Dest <- Origin + /// NOTE: Origin might be null, when the stored value doesn't come + /// from another region. + const MemRegion *Dest, *Origin; +}; + +class ExpressionHandler; +class StoreHandler; + +/// A generalized component for tracking expressions, values, and stores. +/// +/// Tracker aimes at providing a sensible set of default behaviors that can be +/// used by any checker, while providing mechanisms to hook into any part of the +/// tracking process and insert checker-specific logic. +class Tracker { +private: + using ExpressionHandlerPtr = std::unique_ptr; + using StoreHandlerPtr = std::unique_ptr; + + PathSensitiveBugReport + std::list ExpressionHandlers; + std::list StoreHandlers; + +public: + /// \param Report The bug report to which visitors should be attached. + Tracker(PathSensitiveBugReport ); +
[clang] 92d03c2 - [analyzer] Add forwarding `addVisitor` method
Author: Valeriy Savchenko Date: 2021-06-03T17:10:16+03:00 New Revision: 92d03c20ea71479c78a29da09e377e040d37c3a5 URL: https://github.com/llvm/llvm-project/commit/92d03c20ea71479c78a29da09e377e040d37c3a5 DIFF: https://github.com/llvm/llvm-project/commit/92d03c20ea71479c78a29da09e377e040d37c3a5.diff LOG: [analyzer] Add forwarding `addVisitor` method The majority of all `addVisitor` callers follow the same pattern: addVisitor(std::make_unique(arg1, arg2, ...)); This patches introduces additional overload for `addVisitor` to simplify that pattern: addVisitor(arg1, arg2, ...); Differential Revision: https://reviews.llvm.org/D103457 Added: Modified: clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp clang/lib/StaticAnalyzer/Core/BugReporter.cpp clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h index 27bc0dda1f1ce..0b12ff7075780 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -489,11 +489,16 @@ class PathSensitiveBugReport : public BugReport { /// /// The visitors should be used when the default trace is not sufficient. /// For example, they allow constructing a more elaborate trace. - /// \sa registerConditionVisitor(), registerTrackNullOrUndefValue(), - /// registerFindLastStore(), registerNilReceiverVisitor(), and - /// registerVarDeclsLastStore(). + /// @{ void addVisitor(std::unique_ptr visitor); + template + void addVisitor(Args &&... ConstructorArgs) { +addVisitor( +std::make_unique(std::forward(ConstructorArgs)...)); + } + /// @} + /// Remove all visitors attached to this bug report. void clearVisitors(); diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 1f3a1e1ac7773..e0f0dc35e7a71 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -2123,7 +2123,7 @@ void MallocChecker::HandleMismatchedDealloc(CheckerContext , os.str(), N); R->markInteresting(Sym); R->addRange(Range); -R->addVisitor(std::make_unique(Sym)); +R->addVisitor(Sym); C.emitReport(std::move(R)); } } @@ -2216,7 +2216,7 @@ void MallocChecker::HandleUseAfterFree(CheckerContext , SourceRange Range, R->markInteresting(Sym); R->addRange(Range); -R->addVisitor(std::make_unique(Sym)); +R->addVisitor(Sym); if (AF == AF_InnerBuffer) R->addVisitor(allocation_state::getInnerPointerBRVisitor(Sym)); @@ -2252,7 +2252,7 @@ void MallocChecker::HandleDoubleFree(CheckerContext , SourceRange Range, R->markInteresting(Sym); if (PrevSym) R->markInteresting(PrevSym); -R->addVisitor(std::make_unique(Sym)); +R->addVisitor(Sym); C.emitReport(std::move(R)); } } @@ -2278,7 +2278,7 @@ void MallocChecker::HandleDoubleDelete(CheckerContext , SymbolRef Sym) const { *BT_DoubleDelete, "Attempt to delete released memory", N); R->markInteresting(Sym); -R->addVisitor(std::make_unique(Sym)); +R->addVisitor(Sym); C.emitReport(std::move(R)); } } @@ -2308,7 +2308,7 @@ void MallocChecker::HandleUseZeroAlloc(CheckerContext , SourceRange Range, R->addRange(Range); if (Sym) { R->markInteresting(Sym); - R->addVisitor(std::make_unique(Sym)); + R->addVisitor(Sym); } C.emitReport(std::move(R)); } @@ -2578,7 +2578,7 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N, *BT_Leak[*CheckKind], os.str(), N, LocUsedForUniqueing, AllocNode->getLocationContext()->getDecl()); R->markInteresting(Sym); - R->addVisitor(std::make_unique(Sym, true)); + R->addVisitor(Sym, true); C.emitReport(std::move(R)); } diff --git a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index bc7a8a3b12a1d..fe8f7e7bf69e7 100644 --- a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -170,7 +170,7 @@ class NullabilityChecker auto R = std::make_unique(*BT, Msg, N); if (Region) { R->markInteresting(Region); - R->addVisitor(std::make_unique(Region)); + R->addVisitor(Region); } if (ValueExpr) { R->addRange(ValueExpr->getSourceRange()); diff --git
[clang] 45212de - [analyzer][solver] Prevent use of a null state
Author: Valeriy Savchenko Date: 2021-05-13T20:16:29+03:00 New Revision: 45212dec01b9be90596d8d6fa7586ce8c84e2622 URL: https://github.com/llvm/llvm-project/commit/45212dec01b9be90596d8d6fa7586ce8c84e2622 DIFF: https://github.com/llvm/llvm-project/commit/45212dec01b9be90596d8d6fa7586ce8c84e2622.diff LOG: [analyzer][solver] Prevent use of a null state rdar://77686137 Differential Revision: https://reviews.llvm.org/D102240 Added: clang/test/Analysis/PR50268.c Modified: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 974535952d0f0..e54b9c13b9355 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -1487,15 +1487,18 @@ class RangeConstraintManager : public RangedConstraintManager { // This is an infeasible assumption. return nullptr; -ProgramStateRef NewState = setConstraint(State, Sym, NewConstraint); -if (auto Equality = EqualityInfo::extract(Sym, Int, Adjustment)) { - // If the original assumption is not Sym + Adjustment !=/ Int, - // we should invert IsEquality flag. - Equality->IsEquality = Equality->IsEquality != EQ; - return track(NewState, *Equality); +if (ProgramStateRef NewState = setConstraint(State, Sym, NewConstraint)) { + if (auto Equality = EqualityInfo::extract(Sym, Int, Adjustment)) { +// If the original assumption is not Sym + Adjustment !=/ Int, +// we should invert IsEquality flag. +Equality->IsEquality = Equality->IsEquality != EQ; +return track(NewState, *Equality); + } + + return NewState; } -return NewState; +return nullptr; } ProgramStateRef track(ProgramStateRef State, EqualityInfo ToTrack) { diff --git a/clang/test/Analysis/PR50268.c b/clang/test/Analysis/PR50268.c new file mode 100644 index 0..6e3536b7c23c5 --- /dev/null +++ b/clang/test/Analysis/PR50268.c @@ -0,0 +1,12 @@ +// RUN: %clang_analyze_cc1 -w -analyzer-checker=core -verify %s \ +// RUN:-analyzer-config eagerly-assume=true + +// expected-no-diagnostics + + +int test(unsigned long a, unsigned long c, int b) { + c -= a; + if (0 >= b) {} + c == b; + return c ? 0 : 2; // no-crash +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 602c8b4 - [analyzer][NFC] Fix tests failing after a rebase
Author: Valeriy Savchenko Date: 2021-04-28T18:55:20+03:00 New Revision: 602c8b4db5dbe88be7080b411455538c9b4cd117 URL: https://github.com/llvm/llvm-project/commit/602c8b4db5dbe88be7080b411455538c9b4cd117 DIFF: https://github.com/llvm/llvm-project/commit/602c8b4db5dbe88be7080b411455538c9b4cd117.diff LOG: [analyzer][NFC] Fix tests failing after a rebase Added: Modified: clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist Removed: diff --git a/clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist b/clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist index fb92943d00dc..e9f4f7577649 100644 --- a/clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist +++ b/clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist @@ -6561,12 +6561,12 @@ start - line381 + line391 col3 file0 - line381 + line391 col4 file0 @@ -6574,12 +6574,12 @@ end - line381 + line391 col17 file0 - line381 + line391 col17 file0 @@ -6591,7 +6591,7 @@ kindevent location - line381 + line391 col17 file0 @@ -6599,12 +6599,12 @@ - line381 + line391 col17 file0 - line381 + line391 col37 file0 @@ -6731,7 +6731,7 @@ kindevent location - line381 + line391 col17 file0 @@ -6739,12 +6739,12 @@ - line381 + line391 col17 file0 - line381 + line391 col37 file0 @@ -6764,12 +6764,12 @@ start - line381 + line391 col17 file0 - line381 + line391 col17 file0 @@ -6777,12 +6777,12 @@ end - line381 + line391 col3 file0 - line381 + line391 col4 file0 @@ -6798,12 +6798,12 @@ start - line381 + line391 col3 file0 - line381 + line391 col4 file0 @@ -6811,12 +6811,12 @@ end - line385 + line395 col3 file0 - line385 + line395 col3 file0 @@ -6828,7 +6828,7 @@ kindevent location - line385 + line395 col3 file0 @@ -6836,12 +6836,12 @@ - line385 + line395 col3 file0 - line385 + line395 col20 file0 @@ -6865,7 +6865,7 @@ issue_hash_function_offset1 location - line385 + line395 col3 file0 @@ -6879,10 +6879,10 @@ 219 220 221 -380 -381 -384 -385 +390 +391 +394 +395 @@ -6897,12 +6897,12 @@ start - line390 + line400 col3 file0 - line390 + line400 col4 file0 @@ -6910,12 +6910,12 @@ end - line390 + line400 col17 file0 - line390 + line400 col17 file0 @@ -6927,7 +6927,7 @@ kindevent location - line390 + line400 col17 file0 @@ -6935,12 +6935,12 @@ - line390 + line400 col17 file0 - line390 + line400 col37 file0 @@ -7067,7 +7067,7 @@ kindevent location - line390 + line400 col17 file0 @@ -7075,12 +7075,12 @@ - line390 + line400 col17 file0 - line390 + line400 col37 file0 @@ -7100,12 +7100,12 @@ start - line390 + line400 col17
[clang] ab58238 - [analyzer] Find better description for tracked symbolic values
Author: Valeriy Savchenko Date: 2021-04-28T18:37:38+03:00 New Revision: ab5823867c4aee7f3e02ddfaa217905c87471bf9 URL: https://github.com/llvm/llvm-project/commit/ab5823867c4aee7f3e02ddfaa217905c87471bf9 DIFF: https://github.com/llvm/llvm-project/commit/ab5823867c4aee7f3e02ddfaa217905c87471bf9.diff LOG: [analyzer] Find better description for tracked symbolic values When searching for stores and creating corresponding notes, the analyzer is more specific about the target region of the store as opposed to the stored value. While this description was tweaked for constant and undefined values, it lacked in the most general case of symbolic values. This patch tries to find a memory region, where this value is stored, to use it as a better alias for the value. rdar://76645710 Differential Revision: https://reviews.llvm.org/D101041 Added: Modified: clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist clang/test/Analysis/diagnostics/deref-track-symbolic-region.cpp clang/test/Analysis/osobject-retain-release.cpp clang/test/Analysis/retain-release-path-notes.m clang/test/Analysis/uninit-const.c clang/test/Analysis/uninit-const.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index fd334b0bc9c36..c0526469909b3 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -153,6 +153,28 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) { return E; } +static const MemRegion * +getLocationRegionIfReference(const Expr *E, const ExplodedNode *N, + bool LookingForReference = true) { + if (const auto *DR = dyn_cast(E)) { +if (const auto *VD = dyn_cast(DR->getDecl())) { + if (LookingForReference && !VD->getType()->isReferenceType()) +return nullptr; + return N->getState() + ->getLValue(VD, N->getLocationContext()) + .getAsRegion(); +} + } + + // FIXME: This does not handle other kinds of null references, + // for example, references from FieldRegions: + // struct Wrapper { int }; + // Wrapper w = { *(int *)0 }; + // w.ref = 1; + + return nullptr; +} + /// Comparing internal representations of symbolic values (via /// SVal::operator==()) is a valid way to check if the value was updated, /// unless it's a LazyCompoundVal that may have a diff erent internal @@ -1241,16 +1263,17 @@ static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) { /// Show diagnostics for initializing or declaring a region \p R with a bad value. static void showBRDiagnostics(const char *action, llvm::raw_svector_ostream , - const MemRegion *R, SVal V, const DeclStmt *DS) { - if (R->canPrintPretty()) { -R->printPretty(os); + const MemRegion *NewR, SVal V, + const MemRegion *OldR, const DeclStmt *DS) { + if (NewR->canPrintPretty()) { +NewR->printPretty(os); os << " "; } if (V.getAs()) { bool b = false; -if (R->isBoundable()) { - if (const auto *TR = dyn_cast(R)) { +if (NewR->isBoundable()) { + if (const auto *TR = dyn_cast(NewR)) { if (TR->getValueType()->isObjCObjectPointerType()) { os << action << "nil"; b = true; @@ -1262,29 +1285,31 @@ static void showBRDiagnostics(const char *action, llvm::raw_svector_ostream , } else if (auto CVal = V.getAs()) { os << action << CVal->getValue(); + } else if (OldR && OldR->canPrintPretty()) { +os << action << "the value of "; +OldR->printPretty(os); } else if (DS) { if (V.isUndef()) { - if (isa(R)) { + if (isa(NewR)) { const auto *VD = cast(DS->getSingleDecl()); if (VD->getInit()) { - os << (R->canPrintPretty() ? "initialized" : "Initializing") -<< " to a garbage value"; + os << (NewR->canPrintPretty() ? "initialized" : "Initializing") + << " to a garbage value"; } else { - os << (R->canPrintPretty() ? "declared" : "Declaring") -<< " without an initial value"; + os << (NewR->canPrintPretty() ? "declared" : "Declaring") + << " without an initial value"; } } } else { - os << (R->canPrintPretty() ? "initialized" : "Initialized") -<< " here"; + os << (NewR->canPrintPretty() ? "initialized" : "Initialized") << " here"; } } } /// Display diagnostics for passing bad region as a parameter. -static void showBRParamDiagnostics(llvm::raw_svector_ostream& os, -const VarRegion *VR, -SVal V) { +static void showBRParamDiagnostics(llvm::raw_svector_ostream , +
[clang] e273918 - [analyzer] Track leaking object through stores
Author: Valeriy Savchenko Date: 2021-04-28T18:37:38+03:00 New Revision: e273918038a7aa300cb8e6afebd9714bf647eed0 URL: https://github.com/llvm/llvm-project/commit/e273918038a7aa300cb8e6afebd9714bf647eed0 DIFF: https://github.com/llvm/llvm-project/commit/e273918038a7aa300cb8e6afebd9714bf647eed0.diff LOG: [analyzer] Track leaking object through stores Since we can report memory leaks on one variable, while the originally allocated object was stored into another one, we should explain how did it get there. rdar://76645710 Differential Revision: https://reviews.llvm.org/D100852 Added: Modified: clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist clang/test/Analysis/Inputs/expected-plists/retain-release.m.objc.plist clang/test/Analysis/Inputs/expected-plists/retain-release.m.objcpp.plist clang/test/Analysis/osobject-retain-release.cpp clang/test/Analysis/retain-release-path-notes.m Removed: diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp index 48b5f8d9ba7c9..eaa1e35f62bef 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -616,7 +616,7 @@ static Optional describeRegion(const MemRegion *MR) { return None; } -using Bindings = llvm::SmallVector; +using Bindings = llvm::SmallVector, 4>; class VarBindingsCollector : public StoreManager::BindingsHandler { SymbolRef Sym; @@ -633,7 +633,7 @@ class VarBindingsCollector : public StoreManager::BindingsHandler { return true; if (isa(R)) - Result.push_back(R); + Result.emplace_back(R, Val); return true; } @@ -968,11 +968,28 @@ void RefLeakReport::findBindingToReport(CheckerContext , // `AllocFirstBinding` to be one of them. In situations like this, // it would still be the easiest case to explain to our users. if (!AllVarBindings.empty() && - llvm::count(AllVarBindings, AllocFirstBinding) == 0) + llvm::count_if(AllVarBindings, + [this](const std::pair Binding) { + return Binding.first == AllocFirstBinding; + }) == 0) { // Let's pick one of them at random (if there is something to pick from). -AllocBindingToReport = AllVarBindings[0]; - else +AllocBindingToReport = AllVarBindings[0].first; + +// Because 'AllocBindingToReport' is not the the same as +// 'AllocFirstBinding', we need to explain how the leaking object +// got from one to another. +// +// NOTE: We use the actual SVal stored in AllocBindingToReport here because +// FindLastStoreBRVisitor compares SVal's and it can get trickier for +// something like derived regions if we want to construct SVal from +// Sym. Instead, we take the value that is definitely stored in that +// region, thus guaranteeing that FindLastStoreBRVisitor will work. +addVisitor(std::make_unique( +AllVarBindings[0].second.castAs(), AllocBindingToReport, +false, bugreporter::TrackingKind::Thorough)); + } else { AllocBindingToReport = AllocFirstBinding; + } } RefLeakReport::RefLeakReport(const RefCountBug , const LangOptions , diff --git a/clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist b/clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist index 0665e976efe9d..62cd52b7aa822 100644 --- a/clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist +++ b/clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist @@ -21827,6 +21827,35 @@ + + kindevent + location + + line566 + col3 + file0 + + ranges + + + + line566 + col3 + file0 + + + line566 + col11 + file0 + + + + depth0 + extended_message + garply initialized here + message + garply initialized here + kindcontrol edges diff --git a/clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist b/clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist index 185525c3a8473..3b0ce877b76e6 100644 --- a/clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist +++ b/clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist @@ -5155,6 +5155,35 @@ message Method returns an instance of MyObj with a +1 retain count + + kindevent + location + + line215 + col3 +
[clang] 61ae2db - [analyzer] Adjust the reported variable name in retain count checker
Author: Valeriy Savchenko Date: 2021-04-28T18:37:37+03:00 New Revision: 61ae2db2d7a97af5206afe609a303071711f9e6e URL: https://github.com/llvm/llvm-project/commit/61ae2db2d7a97af5206afe609a303071711f9e6e DIFF: https://github.com/llvm/llvm-project/commit/61ae2db2d7a97af5206afe609a303071711f9e6e.diff LOG: [analyzer] Adjust the reported variable name in retain count checker When reporting leaks, we try to attach the leaking object to some variable, so it's easier to understand. Before the patch, we always tried to use the first variable that stored the object in question. This can get very confusing for the user, if that variable doesn't contain that object at the moment of the actual leak. In many cases, the warning is dismissed as false positive and it is effectively a false positive when we fail to properly explain the warning to the user. This patch addresses the bigest issue in cases like this. Now we check if the variable still contains the leaking symbolic object. If not, we look for the last variable to actually hold it and use that variable instead. rdar://76645710 Differential Revision: https://reviews.llvm.org/D100839 Added: Modified: clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h clang/test/Analysis/Inputs/expected-plists/edges-new.mm.plist clang/test/Analysis/Inputs/expected-plists/retain-release-path-notes.m.plist clang/test/Analysis/Inputs/expected-plists/retain-release.m.objc.plist clang/test/Analysis/Inputs/expected-plists/retain-release.m.objcpp.plist clang/test/Analysis/osobject-retain-release.cpp clang/test/Analysis/retain-release-path-notes.m clang/test/Analysis/retain-release.m Removed: diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp index a004d78bfe397..48b5f8d9ba7c9 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -13,6 +13,8 @@ #include "RetainCountDiagnostics.h" #include "RetainCountChecker.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" using namespace clang; using namespace ento; @@ -337,15 +339,15 @@ class RefCountReportVisitor : public BugReporterVisitor { class RefLeakReportVisitor : public RefCountReportVisitor { public: - RefLeakReportVisitor(SymbolRef Sym, const MemRegion *FirstBinding) - : RefCountReportVisitor(Sym), FirstBinding(FirstBinding) {} + RefLeakReportVisitor(SymbolRef Sym, const MemRegion *LastBinding) + : RefCountReportVisitor(Sym), LastBinding(LastBinding) {} PathDiagnosticPieceRef getEndPath(BugReporterContext , const ExplodedNode *N, PathSensitiveBugReport ) override; private: - const MemRegion *FirstBinding; + const MemRegion *LastBinding; }; } // end namespace retaincountchecker @@ -614,6 +616,41 @@ static Optional describeRegion(const MemRegion *MR) { return None; } +using Bindings = llvm::SmallVector; + +class VarBindingsCollector : public StoreManager::BindingsHandler { + SymbolRef Sym; + Bindings + +public: + VarBindingsCollector(SymbolRef Sym, Bindings ) + : Sym(Sym), Result(ToFill) {} + + bool HandleBinding(StoreManager , Store Store, const MemRegion *R, + SVal Val) override { +SymbolRef SymV = Val.getAsLocSymbol(); +if (!SymV || SymV != Sym) + return true; + +if (isa(R)) + Result.push_back(R); + +return true; + } +}; + +Bindings getAllVarBindingsForSymbol(ProgramStateManager , +const ExplodedNode *Node, SymbolRef Sym) { + Bindings Result; + VarBindingsCollector Collector{Sym, Result}; + while (Result.empty() && Node) { +Manager.iterBindings(Node->getState(), Collector); +Node = Node->getFirstPred(); + } + + return Result; +} + namespace { // Find the first node in the current function context that referred to the // tracked symbol and the memory location that value was stored to. Note, the @@ -740,7 +777,7 @@ RefLeakReportVisitor::getEndPath(BugReporterContext , os << "Object leaked: "; - Optional RegionDescription = describeRegion(FirstBinding); + Optional RegionDescription = describeRegion(LastBinding); if (RegionDescription) { os << "object allocated and stored into '" << *RegionDescription << '\''; } else { @@ -749,7 +786,7 @@ RefLeakReportVisitor::getEndPath(BugReporterContext , } // Get the retain count. - const RefVal* RV = getRefBinding(EndN->getState(), Sym); + const RefVal *RV = getRefBinding(EndN->getState(), Sym); assert(RV); if
[clang] 1dad8c5 - [analyzer][NFC] Remove duplicated work from retain count leak report
Author: Valeriy Savchenko Date: 2021-04-28T18:37:37+03:00 New Revision: 1dad8c5036bc912078f7859d89e3ea571616fc4a URL: https://github.com/llvm/llvm-project/commit/1dad8c5036bc912078f7859d89e3ea571616fc4a DIFF: https://github.com/llvm/llvm-project/commit/1dad8c5036bc912078f7859d89e3ea571616fc4a.diff LOG: [analyzer][NFC] Remove duplicated work from retain count leak report Allocation site is the key location for the leak checker. It is a uniqueing location for the report and a source of information for the warning's message. Before this patch, we calculated and used it twice in bug report and in bug report visitor. Such duplication is not only harmful performance-wise (not much, but still), but also design-wise. Because changing something about the end piece of the report should've been repeated for description as well. Differential Revision: https://reviews.llvm.org/D100626 Added: Modified: clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp index 1fc3ee03d2e10..a004d78bfe397 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -337,11 +337,15 @@ class RefCountReportVisitor : public BugReporterVisitor { class RefLeakReportVisitor : public RefCountReportVisitor { public: - RefLeakReportVisitor(SymbolRef sym) : RefCountReportVisitor(sym) {} + RefLeakReportVisitor(SymbolRef Sym, const MemRegion *FirstBinding) + : RefCountReportVisitor(Sym), FirstBinding(FirstBinding) {} PathDiagnosticPieceRef getEndPath(BugReporterContext , const ExplodedNode *N, PathSensitiveBugReport ) override; + +private: + const MemRegion *FirstBinding; }; } // end namespace retaincountchecker @@ -729,14 +733,6 @@ RefLeakReportVisitor::getEndPath(BugReporterContext , // assigned to diff erent variables, etc. BR.markInteresting(Sym); - // We are reporting a leak. Walk up the graph to get to the first node where - // the symbol appeared, and also get the first VarDecl that tracked object - // is stored to. - AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym); - - const MemRegion* FirstBinding = AllocI.R; - BR.markInteresting(AllocI.InterestingMethodContext); - PathDiagnosticLocation L = cast(BR).getEndOfPath(); std::string sbuf; @@ -902,15 +898,15 @@ void RefLeakReport::createDescription(CheckerContext ) { } RefLeakReport::RefLeakReport(const RefCountBug , const LangOptions , - ExplodedNode *n, SymbolRef sym, + ExplodedNode *N, SymbolRef Sym, CheckerContext ) -: RefCountReport(D, LOpts, n, sym, /*isLeak=*/true) { +: RefCountReport(D, LOpts, N, Sym, /*isLeak=*/true) { - deriveAllocLocation(Ctx, sym); + deriveAllocLocation(Ctx, Sym); if (!AllocBinding) -deriveParamLocation(Ctx, sym); +deriveParamLocation(Ctx, Sym); createDescription(Ctx); - addVisitor(std::make_unique(sym)); + addVisitor(std::make_unique(Sym, AllocBinding)); } ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 5780dbe - [-Wcalled-once] Do not run analysis on Obj-C++
Author: Valeriy Savchenko Date: 2021-04-22T15:20:52+03:00 New Revision: 5780dbeee6480fdceae66fb57dcc7bd1cfcda5c9 URL: https://github.com/llvm/llvm-project/commit/5780dbeee6480fdceae66fb57dcc7bd1cfcda5c9 DIFF: https://github.com/llvm/llvm-project/commit/5780dbeee6480fdceae66fb57dcc7bd1cfcda5c9.diff LOG: [-Wcalled-once] Do not run analysis on Obj-C++ Objective-C++ is not yet suppoerted. rdar://76729552 Differential Revision: https://reviews.llvm.org/D100955 Added: Modified: clang/lib/Sema/AnalysisBasedWarnings.cpp clang/test/SemaObjCXX/warn-called-once.mm Removed: diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index bcd6a00d7ba5a..aa2602c8d9256 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -2403,7 +2403,7 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( } // Check for violations of "called once" parameter properties. - if (S.getLangOpts().ObjC && + if (S.getLangOpts().ObjC && !S.getLangOpts().CPlusPlus && shouldAnalyzeCalledOnceParameters(Diags, D->getBeginLoc())) { if (AC.getCFG()) { CalledOnceCheckReporter Reporter(S, IPData->CalledOnceData); diff --git a/clang/test/SemaObjCXX/warn-called-once.mm b/clang/test/SemaObjCXX/warn-called-once.mm index 312da27d9ae32..3763a9b219f24 100644 --- a/clang/test/SemaObjCXX/warn-called-once.mm +++ b/clang/test/SemaObjCXX/warn-called-once.mm @@ -1,7 +1,13 @@ -// RUN: %clang_cc1 -verify -fsyntax-only -Wcompletion-handler %s +// RUN: %clang_cc1 -verify -fsyntax-only -fblocks -Wcompletion-handler %s // expected-no-diagnostics class HasCtor { HasCtor(void *) {} }; + +void double_call_one_block(void (^completionHandler)(void)) { + completionHandler(); + completionHandler(); + // no-warning - we don't support C++/Obj-C++ yet +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 663ac91 - [analyzer] Fix false positives in inner pointer checker (PR49628)
Author: Valeriy Savchenko Date: 2021-04-08T20:30:12+03:00 New Revision: 663ac91ed1d6156e848e5f5f00cd7e7dd6cf867f URL: https://github.com/llvm/llvm-project/commit/663ac91ed1d6156e848e5f5f00cd7e7dd6cf867f DIFF: https://github.com/llvm/llvm-project/commit/663ac91ed1d6156e848e5f5f00cd7e7dd6cf867f.diff LOG: [analyzer] Fix false positives in inner pointer checker (PR49628) This patch supports std::data and std::addressof functions. rdar://73463300 Differential Revision: https://reviews.llvm.org/D99260 Added: Modified: clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp clang/test/Analysis/inner-pointer.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp index 65e52e139ee4..bcae73378028 100644 --- a/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp @@ -34,9 +34,9 @@ namespace { class InnerPointerChecker : public Checker { - CallDescription AppendFn, AssignFn, ClearFn, CStrFn, DataFn, EraseFn, - InsertFn, PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn, - ShrinkToFitFn, SwapFn; + CallDescription AppendFn, AssignFn, AddressofFn, ClearFn, CStrFn, DataFn, + DataMemberFn, EraseFn, InsertFn, PopBackFn, PushBackFn, ReplaceFn, + ReserveFn, ResizeFn, ShrinkToFitFn, SwapFn; public: class InnerPointerBRVisitor : public BugReporterVisitor { @@ -73,9 +73,10 @@ class InnerPointerChecker InnerPointerChecker() : AppendFn({"std", "basic_string", "append"}), AssignFn({"std", "basic_string", "assign"}), +AddressofFn({"std", "addressof"}), ClearFn({"std", "basic_string", "clear"}), -CStrFn({"std", "basic_string", "c_str"}), -DataFn({"std", "basic_string", "data"}), +CStrFn({"std", "basic_string", "c_str"}), DataFn({"std", "data"}, 1), +DataMemberFn({"std", "basic_string", "data"}), EraseFn({"std", "basic_string", "erase"}), InsertFn({"std", "basic_string", "insert"}), PopBackFn({"std", "basic_string", "pop_back"}), @@ -90,6 +91,9 @@ class InnerPointerChecker /// pointers referring to the container object's inner buffer. bool isInvalidatingMemberFunction(const CallEvent ) const; + /// Check whether the called function returns a raw inner pointer. + bool isInnerPointerAccessFunction(const CallEvent ) const; + /// Mark pointer symbols associated with the given memory region released /// in the program state. void markPtrSymbolsReleased(const CallEvent , ProgramStateRef State, @@ -130,6 +134,12 @@ bool InnerPointerChecker::isInvalidatingMemberFunction( Call.isCalled(SwapFn)); } +bool InnerPointerChecker::isInnerPointerAccessFunction( +const CallEvent ) const { + return (Call.isCalled(CStrFn) || Call.isCalled(DataFn) || + Call.isCalled(DataMemberFn)); +} + void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent , ProgramStateRef State, const MemRegion *MR, @@ -172,6 +182,11 @@ void InnerPointerChecker::checkFunctionArguments(const CallEvent , if (!ArgRegion) continue; + // std::addressof function accepts a non-const reference as an argument, + // but doesn't modify it. + if (Call.isCalled(AddressofFn)) +continue; + markPtrSymbolsReleased(Call, State, ArgRegion, C); } } @@ -195,36 +210,49 @@ void InnerPointerChecker::checkPostCall(const CallEvent , CheckerContext ) const { ProgramStateRef State = C.getState(); + // TODO: Do we need these to be typed? + const TypedValueRegion *ObjRegion = nullptr; + if (const auto *ICall = dyn_cast()) { -// TODO: Do we need these to be typed? -const auto *ObjRegion = dyn_cast_or_null( +ObjRegion = dyn_cast_or_null( ICall->getCXXThisVal().getAsRegion()); -if (!ObjRegion) - return; -if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) { - SVal RawPtr = Call.getReturnValue(); - if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) { -// Start tracking this raw pointer by adding it to the set of symbols -// associated with this container object in the program state map. +// Check [string.require] / second point. +if (isInvalidatingMemberFunction(Call)) { + markPtrSymbolsReleased(Call, State, ObjRegion, C); + return; +} + } -PtrSet::Factory = State->getStateManager().get_context(); -const PtrSet *SetPtr = State->get(ObjRegion); -PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet(); -assert(C.wasInlined || !Set.contains(Sym)); -Set = F.add(Set, Sym); + if (isInnerPointerAccessFunction(Call)) { -
[clang] 4b958dd - [analyzer] Fix crash on spaceship operator (PR47511)
Author: Valeriy Savchenko Date: 2021-04-08T20:28:05+03:00 New Revision: 4b958dd6bccab386be432cac99332b867ab9ee22 URL: https://github.com/llvm/llvm-project/commit/4b958dd6bccab386be432cac99332b867ab9ee22 DIFF: https://github.com/llvm/llvm-project/commit/4b958dd6bccab386be432cac99332b867ab9ee22.diff LOG: [analyzer] Fix crash on spaceship operator (PR47511) rdar://68954187 Differential Revision: https://reviews.llvm.org/D99181 Added: clang/test/Analysis/PR47511.cpp Modified: clang/lib/StaticAnalyzer/Core/SValBuilder.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp index a47a28e1e866..9942b7e1423c 100644 --- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -423,6 +423,14 @@ SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, return UnknownVal(); } + if (op == BinaryOperatorKind::BO_Cmp) { +// We can't reason about C++20 spaceship operator yet. +// +// FIXME: Support C++20 spaceship operator. +//The main problem here is that the result is not integer. +return UnknownVal(); + } + if (Optional LV = lhs.getAs()) { if (Optional RV = rhs.getAs()) return evalBinOpLL(state, op, *LV, *RV, type); diff --git a/clang/test/Analysis/PR47511.cpp b/clang/test/Analysis/PR47511.cpp new file mode 100644 index ..d42799f4fbde --- /dev/null +++ b/clang/test/Analysis/PR47511.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_analyze_cc1 -std=c++20 -w -analyzer-checker=core -verify %s + +// expected-no-diagnostics + +namespace std { +struct strong_ordering { + int n; + constexpr operator int() const { return n; } + static const strong_ordering equal, greater, less; +}; +constexpr strong_ordering strong_ordering::equal = {0}; +constexpr strong_ordering strong_ordering::greater = {1}; +constexpr strong_ordering strong_ordering::less = {-1}; +} // namespace std + +void test() { + // no crash + (void)(0 <=> 0); +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 9f0d8ba - [analyzer] Fix dead store checker false positive
Author: Valeriy Savchenko Date: 2021-04-08T16:12:42+03:00 New Revision: 9f0d8bac144c8eb1ca4aff823b2e2d5a0f990072 URL: https://github.com/llvm/llvm-project/commit/9f0d8bac144c8eb1ca4aff823b2e2d5a0f990072 DIFF: https://github.com/llvm/llvm-project/commit/9f0d8bac144c8eb1ca4aff823b2e2d5a0f990072.diff LOG: [analyzer] Fix dead store checker false positive It is common to zero-initialize not only scalar variables, but also structs. This is also defensive programming and we shouldn't complain about that. rdar://34122265 Differential Revision: https://reviews.llvm.org/D99262 Added: Modified: clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp clang/test/Analysis/dead-stores.c Removed: diff --git a/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index 8c86e83608b1e..8070d869f6785 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -11,17 +11,18 @@ // //===--===// -#include "clang/Lex/Lexer.h" -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/ParentMap.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Lex/Lexer.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/ADT/BitVector.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/SaveAndRestore.h" @@ -408,15 +409,17 @@ class DeadStoreObs : public LiveVariables::Observer { // Special case: check for initializations with constants. // // e.g. : int x = 0; + // struct A = {0, 1}; + // struct B = {{0}, {1, 2}}; // // If x is EVER assigned a new value later, don't issue // a warning. This is because such initialization can be // due to defensive programming. - if (E->isEvaluatable(Ctx)) + if (isConstant(E)) return; if (const DeclRefExpr *DRE = - dyn_cast(E->IgnoreParenCasts())) + dyn_cast(E->IgnoreParenCasts())) if (const VarDecl *VD = dyn_cast(DRE->getDecl())) { // Special case: check for initialization from constant // variables. @@ -444,6 +447,29 @@ class DeadStoreObs : public LiveVariables::Observer { } } } + +private: + /// Return true if the given init list can be interpreted as constant + bool isConstant(const InitListExpr *Candidate) const { +// We consider init list to be constant if each member of the list can be +// interpreted as constant. +return llvm::all_of(Candidate->inits(), +[this](const Expr *Init) { return isConstant(Init); }); + } + + /// Return true if the given expression can be interpreted as constant + bool isConstant(const Expr *E) const { +// It looks like E itself is a constant +if (E->isEvaluatable(Ctx)) + return true; + +// We should also allow defensive initialization of structs, i.e. { 0 } +if (const auto *ILE = dyn_cast(E->IgnoreParenCasts())) { + return isConstant(ILE); +} + +return false; + } }; } // end anonymous namespace diff --git a/clang/test/Analysis/dead-stores.c b/clang/test/Analysis/dead-stores.c index a17e1692496da..145b81bd03327 100644 --- a/clang/test/Analysis/dead-stores.c +++ b/clang/test/Analysis/dead-stores.c @@ -635,3 +635,44 @@ void testVolatile() { volatile int v; v = 0; // no warning } + +struct Foo { + int x; + int y; +}; + +struct Foo rdar34122265_getFoo(void); + +int rdar34122265_test(int input) { + // This is allowed for defensive programming. + struct Foo foo = {0, 0}; + if (input > 0) { +foo = rdar34122265_getFoo(); + } else { +return 0; + } + return foo.x + foo.y; +} + +void rdar34122265_test_cast() { + // This is allowed for defensive programming. + struct Foo foo = {0, 0}; + (void)foo; +} + +struct Bar { + struct Foo x, y; +}; + +struct Bar rdar34122265_getBar(void); + +int rdar34122265_test_nested(int input) { + // This is allowed for defensive programming. + struct Bar bar = {{0, 0}, {0, 0}}; + if (input > 0) { +bar = rdar34122265_getBar(); + } else { +return 0; + } + return bar.x.x + bar.y.y; +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org
[clang] 77f1e09 - [-Wcompletion-handler] Don't recognize init methods as conventional
Author: Valeriy Savchenko Date: 2021-04-07T13:50:01+03:00 New Revision: 77f1e096e8a0a0f37a4c5f8a0bacc7c60f44f0a1 URL: https://github.com/llvm/llvm-project/commit/77f1e096e8a0a0f37a4c5f8a0bacc7c60f44f0a1 DIFF: https://github.com/llvm/llvm-project/commit/77f1e096e8a0a0f37a4c5f8a0bacc7c60f44f0a1.diff LOG: [-Wcompletion-handler] Don't recognize init methods as conventional rdar://75704162 Differential Revision: https://reviews.llvm.org/D99601 Added: Modified: clang/lib/Analysis/CalledOnceCheck.cpp clang/test/SemaObjC/warn-called-once.m Removed: diff --git a/clang/lib/Analysis/CalledOnceCheck.cpp b/clang/lib/Analysis/CalledOnceCheck.cpp index 9fa8ac30404c3..db094129a9608 100644 --- a/clang/lib/Analysis/CalledOnceCheck.cpp +++ b/clang/lib/Analysis/CalledOnceCheck.cpp @@ -1011,11 +1011,16 @@ class CalledOnceChecker : public ConstStmtVisitor { return llvm::None; } + /// Return true if the specified selector represents init method. + static bool isInitMethod(Selector MethodSelector) { +return MethodSelector.getMethodFamily() == OMF_init; + } + /// Return true if the specified selector piece matches conventions. static bool isConventionalSelectorPiece(Selector MethodSelector, unsigned PieceIndex, QualType PieceType) { -if (!isConventional(PieceType)) { +if (!isConventional(PieceType) || isInitMethod(MethodSelector)) { return false; } diff --git a/clang/test/SemaObjC/warn-called-once.m b/clang/test/SemaObjC/warn-called-once.m index ff2778d4bd0a4..27030dd94a825 100644 --- a/clang/test/SemaObjC/warn-called-once.m +++ b/clang/test/SemaObjC/warn-called-once.m @@ -13,6 +13,7 @@ @protocol NSObject @end @interface NSObject +- (instancetype)init; - (id)copy; - (id)class; - autorelease; @@ -1235,4 +1236,13 @@ - (void)test_cleanup_2:(int)cond handler(); // expected-warning{{completion handler is called twice}} } +- (void)initWithAdditions:(int)cond + withCompletion:(void (^)(void))handler { + self = [self init]; + if (self) { +escape(handler); + } + // no-warning +} + @end ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 4821c15 - [analyzer] Fix body farm for Obj-C++ properties
Author: Valeriy Savchenko Date: 2021-04-07T13:44:43+03:00 New Revision: 4821c15691bab9efaef871c957a8ba73697cdda9 URL: https://github.com/llvm/llvm-project/commit/4821c15691bab9efaef871c957a8ba73697cdda9 DIFF: https://github.com/llvm/llvm-project/commit/4821c15691bab9efaef871c957a8ba73697cdda9.diff LOG: [analyzer] Fix body farm for Obj-C++ properties When property is declared in a superclass (or in a protocol), it still can be of CXXRecord type and Sema could've already generated a body for us. This patch joins two branches and two ways of acquiring IVar in order to reuse the existing code. And prevent us from generating l-value to r-value casts for C++ types. rdar://67416721 Differential Revision: https://reviews.llvm.org/D99194 Added: Modified: clang/lib/Analysis/BodyFarm.cpp clang/test/Analysis/properties.mm Removed: diff --git a/clang/lib/Analysis/BodyFarm.cpp b/clang/lib/Analysis/BodyFarm.cpp index 603da67156254..b2110f6b5450c 100644 --- a/clang/lib/Analysis/BodyFarm.cpp +++ b/clang/lib/Analysis/BodyFarm.cpp @@ -742,8 +742,9 @@ static const ObjCIvarDecl *findBackingIvar(const ObjCPropertyDecl *Prop) { static Stmt *createObjCPropertyGetter(ASTContext , const ObjCMethodDecl *MD) { -// First, find the backing ivar. + // First, find the backing ivar. const ObjCIvarDecl *IVar = nullptr; + const ObjCPropertyDecl *Prop = nullptr; // Property accessor stubs sometimes do not correspond to any property decl // in the current interface (but in a superclass). They still have a @@ -751,54 +752,57 @@ static Stmt *createObjCPropertyGetter(ASTContext , if (MD->isSynthesizedAccessorStub()) { const ObjCInterfaceDecl *IntD = MD->getClassInterface(); const ObjCImplementationDecl *ImpD = IntD->getImplementation(); -for (const auto *PI: ImpD->property_impls()) { - if (const ObjCPropertyDecl *P = PI->getPropertyDecl()) { -if (P->getGetterName() == MD->getSelector()) - IVar = P->getPropertyIvarDecl(); +for (const auto *PI : ImpD->property_impls()) { + if (const ObjCPropertyDecl *Candidate = PI->getPropertyDecl()) { +if (Candidate->getGetterName() == MD->getSelector()) { + Prop = Candidate; + IVar = Prop->getPropertyIvarDecl(); +} } } } if (!IVar) { -const ObjCPropertyDecl *Prop = MD->findPropertyDecl(); +Prop = MD->findPropertyDecl(); IVar = findBackingIvar(Prop); -if (!IVar) - return nullptr; + } -// Ignore weak variables, which have special behavior. -if (Prop->getPropertyAttributes() & ObjCPropertyAttribute::kind_weak) - return nullptr; + if (!IVar || !Prop) +return nullptr; + + // Ignore weak variables, which have special behavior. + if (Prop->getPropertyAttributes() & ObjCPropertyAttribute::kind_weak) +return nullptr; -// Look to see if Sema has synthesized a body for us. This happens in -// Objective-C++ because the return value may be a C++ class type with a -// non-trivial copy constructor. We can only do this if we can find the -// @synthesize for this property, though (or if we know it's been auto- -// synthesized). -const ObjCImplementationDecl *ImplDecl = + // Look to see if Sema has synthesized a body for us. This happens in + // Objective-C++ because the return value may be a C++ class type with a + // non-trivial copy constructor. We can only do this if we can find the + // @synthesize for this property, though (or if we know it's been auto- + // synthesized). + const ObjCImplementationDecl *ImplDecl = IVar->getContainingInterface()->getImplementation(); -if (ImplDecl) { - for (const auto *I : ImplDecl->property_impls()) { -if (I->getPropertyDecl() != Prop) - continue; - -if (I->getGetterCXXConstructor()) { - ASTMaker M(Ctx); - return M.makeReturn(I->getGetterCXXConstructor()); -} + if (ImplDecl) { +for (const auto *I : ImplDecl->property_impls()) { + if (I->getPropertyDecl() != Prop) +continue; + + if (I->getGetterCXXConstructor()) { +ASTMaker M(Ctx); +return M.makeReturn(I->getGetterCXXConstructor()); } } - -// Sanity check that the property is the same type as the ivar, or a -// reference to it, and that it is either an object pointer or trivially -// copyable. -if (!Ctx.hasSameUnqualifiedType(IVar->getType(), -Prop->getType().getNonReferenceType())) - return nullptr; -if (!IVar->getType()->isObjCLifetimeType() && -!IVar->getType().isTriviallyCopyableType(Ctx)) - return nullptr; } + // Sanity check that the property is the same type as the ivar, or a + // reference to it, and that it is either an object pointer or trivially + // copyable. + if
[clang] af7e1f0 - [analyzer] Fix crash when reasoning about C11 atomics (PR49422)
Author: Valeriy Savchenko Date: 2021-03-30T16:04:19+03:00 New Revision: af7e1f07ac03074647897498358aaec165c1aaea URL: https://github.com/llvm/llvm-project/commit/af7e1f07ac03074647897498358aaec165c1aaea DIFF: https://github.com/llvm/llvm-project/commit/af7e1f07ac03074647897498358aaec165c1aaea.diff LOG: [analyzer] Fix crash when reasoning about C11 atomics (PR49422) rdar://75020762 Differential Revision: https://reviews.llvm.org/D99274 Added: Modified: clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h clang/test/Analysis/atomics.c Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h index 9f464e82304f4..f59b254094db8 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h @@ -139,6 +139,12 @@ class BasicValueFactory { /// Returns the type of the APSInt used to store values of the given QualType. APSIntType getAPSIntType(QualType T) const { +// For the purposes of the analysis and constraints, we treat atomics +// as their underlying types. +if (const AtomicType *AT = T->getAs()) { + T = AT->getValueType(); +} + assert(T->isIntegralOrEnumerationType() || Loc::isLocType(T)); return APSIntType(Ctx.getIntWidth(T), !T->isSignedIntegerOrEnumerationType()); diff --git a/clang/test/Analysis/atomics.c b/clang/test/Analysis/atomics.c index b3d2d352a2283..ef1a216c7d577 100644 --- a/clang/test/Analysis/atomics.c +++ b/clang/test/Analysis/atomics.c @@ -93,3 +93,11 @@ void test_atomic_compare_exchange_weak(struct RefCountedStruct *s) { clang_analyzer_eval(s->refCount == 3); // expected-warning {{UNKNOWN}} clang_analyzer_eval(expected == 2); // expected-warning {{UNKNOWN}} } + +// PR49422 +void test_atomic_compare(int input) { + _Atomic(int) x = input; + if (x > 0) { +// no crash + } +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 9037730 - [analyzer] Support allocClassWithName in OSObjectCStyleCast checker
Author: Valeriy Savchenko Date: 2021-03-30T15:58:06+03:00 New Revision: 90377308de6cac8239bc1a1dcd32b57b9ec91444 URL: https://github.com/llvm/llvm-project/commit/90377308de6cac8239bc1a1dcd32b57b9ec91444 DIFF: https://github.com/llvm/llvm-project/commit/90377308de6cac8239bc1a1dcd32b57b9ec91444.diff LOG: [analyzer] Support allocClassWithName in OSObjectCStyleCast checker `allocClassWithName` allocates an object with the given type. The type is actually provided as a string argument (type's name). This creates a possibility for not particularly useful warnings from the analyzer. In order to combat with those, this patch checks for casts of the `allocClassWithName` results to types mentioned directly as its argument. All other uses of this method should be reasoned about as before. rdar://72165694 Differential Revision: https://reviews.llvm.org/D99500 Added: Modified: clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp clang/test/Analysis/os_object_base.h clang/test/Analysis/osobjectcstylecastchecker_test.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp b/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp index 270b66dab020..0a8379d9ab99 100644 --- a/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp @@ -32,7 +32,21 @@ class OSObjectCStyleCastChecker : public Checker { void checkASTCodeBody(const Decl *D, AnalysisManager , BugReporter ) const; }; +} // namespace + +namespace clang { +namespace ast_matchers { +AST_MATCHER_P(StringLiteral, mentionsBoundType, std::string, BindingID) { + return Builder->removeBindings([this, ](const BoundNodesMap ) { +const auto = Nodes.getNode(this->BindingID); +if (const auto *ND = BN.get()) { + return ND->getName() != Node.getString(); +} +return true; + }); } +} // end namespace ast_matchers +} // end namespace clang static void emitDiagnostics(const BoundNodes , BugReporter , @@ -63,22 +77,41 @@ static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM) { return hasType(pointerType(pointee(hasDeclaration(DeclM; } -void OSObjectCStyleCastChecker::checkASTCodeBody(const Decl *D, AnalysisManager , +void OSObjectCStyleCastChecker::checkASTCodeBody(const Decl *D, + AnalysisManager , BugReporter ) const { AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D); auto DynamicCastM = callExpr(callee(functionDecl(hasName("safeMetaCast"; - - auto OSObjTypeM = hasTypePointingTo(cxxRecordDecl(isDerivedFrom("OSMetaClassBase"))); + // 'allocClassWithName' allocates an object with the given type. + // The type is actually provided as a string argument (type's name). + // This makes the following pattern possible: + // + // Foo *object = (Foo *)allocClassWithName("Foo"); + // + // While OSRequiredCast can be used here, it is still not a useful warning. + auto AllocClassWithNameM = callExpr( + callee(functionDecl(hasName("allocClassWithName"))), + // Here we want to make sure that the string argument matches the + // type in the cast expression. + hasArgument(0, stringLiteral(mentionsBoundType(WarnRecordDecl; + + auto OSObjTypeM = + hasTypePointingTo(cxxRecordDecl(isDerivedFrom("OSMetaClassBase"))); auto OSObjSubclassM = hasTypePointingTo( -cxxRecordDecl(isDerivedFrom("OSObject")).bind(WarnRecordDecl)); - - auto CastM = cStyleCastExpr( - allOf(hasSourceExpression(allOf(OSObjTypeM, unless(DynamicCastM))), - OSObjSubclassM)).bind(WarnAtNode); - - auto Matches = match(stmt(forEachDescendant(CastM)), *D->getBody(), AM.getASTContext()); + cxxRecordDecl(isDerivedFrom("OSObject")).bind(WarnRecordDecl)); + + auto CastM = + cStyleCastExpr( + allOf(OSObjSubclassM, +hasSourceExpression( +allOf(OSObjTypeM, + unless(anyOf(DynamicCastM, AllocClassWithNameM)) + .bind(WarnAtNode); + + auto Matches = + match(stmt(forEachDescendant(CastM)), *D->getBody(), AM.getASTContext()); for (BoundNodes Match : Matches) emitDiagnostics(Match, BR, ADC, this); } diff --git a/clang/test/Analysis/os_object_base.h b/clang/test/Analysis/os_object_base.h index 4698185f2b3c..c3d5d6271d48 100644 --- a/clang/test/Analysis/os_object_base.h +++ b/clang/test/Analysis/os_object_base.h @@ -66,6 +66,7 @@ struct OSObject : public OSMetaClassBase { struct OSMetaClass : public OSMetaClassBase { virtual OSObject * alloc() const; + static OSObject * allocClassWithName(const char * name); virtual ~OSMetaClass(){} }; diff --git a/clang/test/Analysis/osobjectcstylecastchecker_test.cpp
[clang] 02b51e5 - [analyzer][solver] Redesign constraint ranges data structure
Author: Valeriy Savchenko Date: 2021-03-22T13:52:35+03:00 New Revision: 02b51e5316cd501ede547d0223e0f5416ebd9845 URL: https://github.com/llvm/llvm-project/commit/02b51e5316cd501ede547d0223e0f5416ebd9845 DIFF: https://github.com/llvm/llvm-project/commit/02b51e5316cd501ede547d0223e0f5416ebd9845.diff LOG: [analyzer][solver] Redesign constraint ranges data structure ImmutableSet doesn't seem like the perfect fit for the RangeSet data structure. It is good for saving memory in a persistent setting, but not for the case when the population of the container is tiny. This commit replaces RangeSet implementation and redesigns the most common operations to be more efficient. Differential Revision: https://reviews.llvm.org/D86465 Added: Modified: clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp clang/unittests/StaticAnalyzer/RangeSetTest.cpp Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h index bc5d5f57cd68..4a118074463d 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h @@ -16,6 +16,8 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/Support/Allocator.h" namespace clang { @@ -24,21 +26,19 @@ namespace ento { /// A Range represents the closed range [from, to]. The caller must /// guarantee that from <= to. Note that Range is immutable, so as not /// to subvert RangeSet's immutability. -class Range : public std::pair { +class Range { public: - Range(const llvm::APSInt , const llvm::APSInt ) - : std::pair(, ) { -assert(from <= to); + Range(const llvm::APSInt , const llvm::APSInt ) : Impl(, ) { +assert(From <= To); } - Range(const llvm::APSInt ) - : std::pair(, ) {} + Range(const llvm::APSInt ) : Range(Point, Point) {} - bool Includes(const llvm::APSInt ) const { -return *first <= v && v <= *second; + bool Includes(const llvm::APSInt ) const { +return From() <= Point && Point <= To(); } - const llvm::APSInt () const { return *first; } - const llvm::APSInt () const { return *second; } + const llvm::APSInt () const { return *Impl.first; } + const llvm::APSInt () const { return *Impl.second; } const llvm::APSInt *getConcreteValue() const { return () == () ? () : nullptr; } @@ -47,93 +47,264 @@ class Range : public std::pair { ID.AddPointer(()); ID.AddPointer(()); } -}; + void dump(raw_ostream ) const; -class RangeTrait : public llvm::ImutContainerInfo { -public: - // When comparing if one Range is less than another, we should compare - // the actual APSInt values instead of their pointers. This keeps the order - // consistent (instead of comparing by pointer values) and can potentially - // be used to speed up some of the operations in RangeSet. - static inline bool isLess(key_type_ref lhs, key_type_ref rhs) { -return *lhs.first < *rhs.first || - (!(*rhs.first < *lhs.first) && *lhs.second < *rhs.second); - } + // In order to keep non-overlapping ranges sorted, we can compare only From + // points. + bool operator<(const Range ) const { return From() < RHS.From(); } + + bool operator==(const Range ) const { return Impl == RHS.Impl; } + bool operator!=(const Range ) const { return !operator==(RHS); } + +private: + std::pair Impl; }; -/// RangeSet contains a set of ranges. If the set is empty, then -/// there the value of a symbol is overly constrained and there are no -/// possible values for that symbol. +/// @class RangeSet is a persistent set of non-overlapping ranges. +/// +/// New RangeSet objects can be ONLY produced by RangeSet::Factory object, which +/// also supports the most common operations performed on range sets. +/// +/// Empty set corresponds to an overly constrained symbol meaning that there +/// are no possible values for that symbol. class RangeSet { - typedef llvm::ImmutableSet PrimRangeSet; - PrimRangeSet ranges; // no need to make const, since it is an - // ImmutableSet - this allows default operator= - // to work. public: - typedef PrimRangeSet::Factory Factory; - typedef PrimRangeSet::iterator iterator; - - RangeSet(PrimRangeSet RS) : ranges(RS) {} - - /// Create a new set with all ranges of this set and RS. - /// Possible intersections are not checked here. - RangeSet addRange(Factory ,
[clang] 3085bda - [analyzer][solver] Fix infeasible constraints (PR49642)
Author: Valeriy Savchenko Date: 2021-03-22T11:02:02+03:00 New Revision: 3085bda2b348f6a8b4e0bd1d230af4e9c900c9c4 URL: https://github.com/llvm/llvm-project/commit/3085bda2b348f6a8b4e0bd1d230af4e9c900c9c4 DIFF: https://github.com/llvm/llvm-project/commit/3085bda2b348f6a8b4e0bd1d230af4e9c900c9c4.diff LOG: [analyzer][solver] Fix infeasible constraints (PR49642) Additionally, this patch puts an assertion checking for feasible constraints in every place where constraints are assigned to states. Differential Revision: https://reviews.llvm.org/D98948 Added: clang/test/Analysis/PR49642.c Modified: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 95e61f9c8c61..6ae80b3ae773 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -448,12 +448,12 @@ class EquivalenceClass : public llvm::FoldingSetNode { EquivalenceClass Other); /// Return a set of class members for the given state. - LLVM_NODISCARD inline SymbolSet getClassMembers(ProgramStateRef State); + LLVM_NODISCARD inline SymbolSet getClassMembers(ProgramStateRef State) const; /// Return true if the current class is trivial in the given state. - LLVM_NODISCARD inline bool isTrivial(ProgramStateRef State); + LLVM_NODISCARD inline bool isTrivial(ProgramStateRef State) const; /// Return true if the current class is trivial and its only member is dead. LLVM_NODISCARD inline bool isTriviallyDead(ProgramStateRef State, - SymbolReaper ); + SymbolReaper ) const; LLVM_NODISCARD static inline ProgramStateRef markDisequal(BasicValueFactory , RangeSet::Factory , @@ -521,7 +521,7 @@ class EquivalenceClass : public llvm::FoldingSetNode { ProgramStateRef State, SymbolSet Members, EquivalenceClass Other, SymbolSet OtherMembers); - static inline void + static inline bool addToDisequalityInfo(DisequalityMapTy , ConstraintRangeTy , BasicValueFactory , RangeSet::Factory , ProgramStateRef State, EquivalenceClass First, @@ -535,6 +535,15 @@ class EquivalenceClass : public llvm::FoldingSetNode { // Constraint functions //===--===// +LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED bool +areFeasible(ConstraintRangeTy Constraints) { + return llvm::none_of( + Constraints, + [](const std::pair ) { +return ClassConstraint.second.isEmpty(); + }); +} + LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State, EquivalenceClass Class) { return State->get(Class); @@ -1397,15 +1406,6 @@ class RangeConstraintManager : public RangedConstraintManager { return EquivalenceClass::merge(getBasicVals(), F, State, LHS, RHS); } - LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED static bool - areFeasible(ConstraintRangeTy Constraints) { -return llvm::none_of( -Constraints, -[](const std::pair ) { - return ClassConstraint.second.isEmpty(); -}); - } - LLVM_NODISCARD ProgramStateRef setConstraint(ProgramStateRef State, EquivalenceClass Class, RangeSet Constraint) { @@ -1428,7 +1428,7 @@ class RangeConstraintManager : public RangedConstraintManager { getRange(State, DisequalClass).Delete(getBasicVals(), F, *Point); // If we end up with at least one of the disequal classes to be -// constrainted with an empty range-set, the state is infeasible. +// constrained with an empty range-set, the state is infeasible. if (UpdatedConstraint.isEmpty()) return nullptr; @@ -1574,6 +1574,9 @@ EquivalenceClass::mergeImpl(BasicValueFactory , // Assign new constraints for this class. Constraints = CRF.add(Constraints, *this, *NewClassConstraint); +assert(areFeasible(Constraints) && "Constraint manager shouldn't produce " + "a state with infeasible constraints"); + State = State->set(Constraints); } @@ -1644,7 +1647,7 @@ EquivalenceClass::getMembersFactory(ProgramStateRef State) { return State->get_context(); } -SymbolSet EquivalenceClass::getClassMembers(ProgramStateRef State) { +SymbolSet EquivalenceClass::getClassMembers(ProgramStateRef State) const { if (const SymbolSet *Members = State->get(*this)) return
[clang] 8b8b9af - [-Wcalled-once-parameter][NFC] Fix GCC compilation error
Author: Valeriy Savchenko Date: 2021-03-18T14:49:24+03:00 New Revision: 8b8b9af8c9132acb446fc42569de8a0f57c6b556 URL: https://github.com/llvm/llvm-project/commit/8b8b9af8c9132acb446fc42569de8a0f57c6b556 DIFF: https://github.com/llvm/llvm-project/commit/8b8b9af8c9132acb446fc42569de8a0f57c6b556.diff LOG: [-Wcalled-once-parameter][NFC] Fix GCC compilation error Added: Modified: clang/lib/Analysis/CalledOnceCheck.cpp Removed: diff --git a/clang/lib/Analysis/CalledOnceCheck.cpp b/clang/lib/Analysis/CalledOnceCheck.cpp index ab56d3e3c988..00bb51a1c0d3 100644 --- a/clang/lib/Analysis/CalledOnceCheck.cpp +++ b/clang/lib/Analysis/CalledOnceCheck.cpp @@ -63,14 +63,14 @@ struct KnownCalledOnceParameter { unsigned ParamIndex; }; constexpr KnownCalledOnceParameter KNOWN_CALLED_ONCE_PARAMETERS[] = { -{"dispatch_async", 1}, -{"dispatch_async_and_wait", 1}, -{"dispatch_after", 2}, -{"dispatch_sync", 1}, -{"dispatch_once", 1}, -{"dispatch_barrier_async", 1}, -{"dispatch_barrier_async_and_wait", 1}, -{"dispatch_barrier_sync", 1}}; +{llvm::StringLiteral{"dispatch_async"}, 1}, +{llvm::StringLiteral{"dispatch_async_and_wait"}, 1}, +{llvm::StringLiteral{"dispatch_after"}, 2}, +{llvm::StringLiteral{"dispatch_sync"}, 1}, +{llvm::StringLiteral{"dispatch_once"}, 1}, +{llvm::StringLiteral{"dispatch_barrier_async"}, 1}, +{llvm::StringLiteral{"dispatch_barrier_async_and_wait"}, 1}, +{llvm::StringLiteral{"dispatch_barrier_sync"}, 1}}; class ParameterStatus { public: ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 4a7afc9 - [-Wcalled-once-parameter] Fix false positives for cleanup attr
Author: Valeriy Savchenko Date: 2021-03-18T12:32:16+03:00 New Revision: 4a7afc9a8843f4793296a260f7153fd2ef4ec497 URL: https://github.com/llvm/llvm-project/commit/4a7afc9a8843f4793296a260f7153fd2ef4ec497 DIFF: https://github.com/llvm/llvm-project/commit/4a7afc9a8843f4793296a260f7153fd2ef4ec497.diff LOG: [-Wcalled-once-parameter] Fix false positives for cleanup attr Cleanup attribute allows users to attach a destructor-like functions to variable declarations to be called whenever they leave the scope. The logic of such functions is not supported by the Clang's CFG and is too hard to be reasoned about. In order to avoid false positives in this situation, we assume that we didn't see ALL of the executtion paths of the function and, thus, can warn only about multiple call violation. rdar://74441906 Differential Revision: https://reviews.llvm.org/D98694 Added: Modified: clang/lib/Analysis/CalledOnceCheck.cpp clang/test/SemaObjC/warn-called-once.m Removed: diff --git a/clang/lib/Analysis/CalledOnceCheck.cpp b/clang/lib/Analysis/CalledOnceCheck.cpp index 29021b0a9016..ab56d3e3c988 100644 --- a/clang/lib/Analysis/CalledOnceCheck.cpp +++ b/clang/lib/Analysis/CalledOnceCheck.cpp @@ -812,8 +812,12 @@ class CalledOnceChecker : public ConstStmtVisitor { } } -// Early exit if we don't have parameters for extra analysis. -if (NotCalledOnEveryPath.none() && NotUsedOnEveryPath.none()) +// Early exit if we don't have parameters for extra analysis... +if (NotCalledOnEveryPath.none() && NotUsedOnEveryPath.none() && +// ... or if we've seen variables with cleanup functions. +// We can't reason that we've seen every path in this case, +// and thus abandon reporting any warnings that imply that. +!FunctionHasCleanupVars) return; // We are looking for a pair of blocks A, B so that the following is true: @@ -1601,6 +1605,10 @@ class CalledOnceChecker : public ConstStmtVisitor { if (Var->getInit()) { checkEscapee(Var->getInit()); } + +if (Var->hasAttr()) { + FunctionHasCleanupVars = true; +} } } } @@ -1669,6 +1677,13 @@ class CalledOnceChecker : public ConstStmtVisitor { // around. bool SuppressOnConventionalErrorPaths = false; + // The user can annotate variable declarations with cleanup functions, which + // essentially imposes a custom destructor logic on that variable. + // It is possible to use it, however, to call tracked parameters on all exits + // from the function. For this reason, we track the fact that the function + // actually has these. + bool FunctionHasCleanupVars = false; + State CurrentState; ParamSizedVector TrackedParams; CFGSizedVector States; diff --git a/clang/test/SemaObjC/warn-called-once.m b/clang/test/SemaObjC/warn-called-once.m index 825d491f53bb..ff2778d4bd0a 100644 --- a/clang/test/SemaObjC/warn-called-once.m +++ b/clang/test/SemaObjC/warn-called-once.m @@ -1193,4 +1193,46 @@ - (void)test_escape_after_branch:(int)cond escape(handler); } +// rdar://74441906 +typedef void (^DeferredBlock)(void); +static inline void DefferedCallback(DeferredBlock *inBlock) { (*inBlock)(); } +#define _DEFERCONCAT(a, b) a##b +#define _DEFERNAME(a) _DEFERCONCAT(__DeferredVar_, a) +#define DEFER __extension__ __attribute__((cleanup(DefferedCallback), unused)) \ + DeferredBlock _DEFERNAME(__COUNTER__) = ^ + +- (void)test_cleanup_1:(int)cond +withCompletion:(void (^)(void))handler { + int error = 0; + DEFER { +if (error) + handler(); + }; + + if (cond) { +error = 1; + } else { +// no-warning +handler(); + } +} + +- (void)test_cleanup_2:(int)cond +withCompletion:(void (^)(void))handler { + int error = 0; + DEFER { +if (error) + handler(); + }; + + if (cond) { +error = 1; + } else { +handler(); // expected-note{{previous call is here}} + } + + // We still can warn about double call even in this case. + handler(); // expected-warning{{completion handler is called twice}} +} + @end ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] f1a7d5a - [-Wcalled-once-parameter] Harden analysis in terms of block use
Author: Valeriy Savchenko Date: 2021-03-18T12:12:18+03:00 New Revision: f1a7d5a7b0ec810057ff6e88371ab86d1fce812c URL: https://github.com/llvm/llvm-project/commit/f1a7d5a7b0ec810057ff6e88371ab86d1fce812c DIFF: https://github.com/llvm/llvm-project/commit/f1a7d5a7b0ec810057ff6e88371ab86d1fce812c.diff LOG: [-Wcalled-once-parameter] Harden analysis in terms of block use This patch introduces a very simple inter-procedural analysis between blocks and enclosing functions. We always analyze blocks first (analysis is done as part of semantic analysis that goes side-by-side with the parsing process), and at the moment of reporting we don't know how that block will be actually used. This patch introduces new logic delaying reports of the "never called" warnings on blocks. If we are not sure that the block will be called exactly once, we shouldn't warn our users about that. Double calls, however, don't require such delays. While analyzing the enclosing function, we can actually decide what we should do with those warnings. Additionally, as a side effect, we can be more confident about blocks in such context and can treat them not as escapes, but as direct calls. rdar://74090107 Differential Revision: https://reviews.llvm.org/D98688 Added: Modified: clang/include/clang/Analysis/Analyses/CalledOnceCheck.h clang/include/clang/Sema/AnalysisBasedWarnings.h clang/lib/Analysis/CalledOnceCheck.cpp clang/lib/Sema/AnalysisBasedWarnings.cpp clang/test/SemaObjC/warn-called-once.m Removed: diff --git a/clang/include/clang/Analysis/Analyses/CalledOnceCheck.h b/clang/include/clang/Analysis/Analyses/CalledOnceCheck.h index fc574c680a44..a0c767bf92d2 100644 --- a/clang/include/clang/Analysis/Analyses/CalledOnceCheck.h +++ b/clang/include/clang/Analysis/Analyses/CalledOnceCheck.h @@ -17,6 +17,7 @@ namespace clang { class AnalysisDeclContext; +class BlockDecl; class CFG; class Decl; class DeclContext; @@ -79,6 +80,7 @@ class CalledOnceCheckHandler { /// the path containing the call and not containing the call. This helps us /// to pinpoint a bad path for the user. /// \param Parameter -- parameter that should be called once. + /// \param Function -- function declaration where the problem occured. /// \param Where -- the least common ancestor statement. /// \param Reason -- a reason describing the path without a call. /// \param IsCalledDirectly -- true, if parameter actually gets called on @@ -86,9 +88,22 @@ class CalledOnceCheckHandler { /// collection, passed as a parameter, etc.). /// \param IsCompletionHandler -- true, if parameter is a completion handler. virtual void handleNeverCalled(const ParmVarDecl *Parameter, - const Stmt *Where, NeverCalledReason Reason, + const Decl *Function, const Stmt *Where, + NeverCalledReason Reason, bool IsCalledDirectly, bool IsCompletionHandler) {} + + /// Called when the block is guaranteed to be called exactly once. + /// It means that we can be stricter with what we report on that block. + /// \param Block -- block declaration that is known to be called exactly once. + virtual void + handleBlockThatIsGuaranteedToBeCalledOnce(const BlockDecl *Block) {} + + /// Called when the block has no guarantees about how many times it can get + /// called. + /// It means that we should be more lenient with reporting warnings in it. + /// \param Block -- block declaration in question. + virtual void handleBlockWithNoGuarantees(const BlockDecl *Block) {} }; /// Check given CFG for 'called once' parameter violations. diff --git a/clang/include/clang/Sema/AnalysisBasedWarnings.h b/clang/include/clang/Sema/AnalysisBasedWarnings.h index e13fe955eaf4..49b69c585ff7 100644 --- a/clang/include/clang/Sema/AnalysisBasedWarnings.h +++ b/clang/include/clang/Sema/AnalysisBasedWarnings.h @@ -14,6 +14,7 @@ #define LLVM_CLANG_SEMA_ANALYSISBASEDWARNINGS_H #include "llvm/ADT/DenseMap.h" +#include namespace clang { @@ -47,6 +48,9 @@ class AnalysisBasedWarnings { Sema Policy DefaultPolicy; + class InterProceduralData; + std::unique_ptr IPData; + enum VisitFlag { NotVisited = 0, Visited = 1, Pending = 2 }; llvm::DenseMap VisitedFD; @@ -88,6 +92,7 @@ class AnalysisBasedWarnings { public: AnalysisBasedWarnings(Sema ); + ~AnalysisBasedWarnings(); void IssueWarnings(Policy P, FunctionScopeInfo *fscope, const Decl *D, QualType BlockType); @@ -97,6 +102,7 @@ class AnalysisBasedWarnings { void PrintStats() const; }; -}} // end namespace clang::sema +} // namespace sema +} // namespace clang #endif diff --git a/clang/lib/Analysis/CalledOnceCheck.cpp b/clang/lib/Analysis/CalledOnceCheck.cpp index
[clang] c86dacd - [-Wcalled-once-parameter] Let escapes overwrite MaybeCalled states
Author: Valeriy Savchenko Date: 2021-03-17T11:12:55+03:00 New Revision: c86dacd1a4489572721dec3135506d31da96c679 URL: https://github.com/llvm/llvm-project/commit/c86dacd1a4489572721dec3135506d31da96c679 DIFF: https://github.com/llvm/llvm-project/commit/c86dacd1a4489572721dec3135506d31da96c679.diff LOG: [-Wcalled-once-parameter] Let escapes overwrite MaybeCalled states This commit makes escapes symmetrical, meaning that having escape before and after the branching, where parameter is not called on one of the paths, will have the same effect. Differential Revision: https://reviews.llvm.org/D98622 Added: Modified: clang/lib/Analysis/CalledOnceCheck.cpp clang/test/SemaObjC/warn-called-once.m Removed: diff --git a/clang/lib/Analysis/CalledOnceCheck.cpp b/clang/lib/Analysis/CalledOnceCheck.cpp index 2438c50d7e4e..d24e0b500564 100644 --- a/clang/lib/Analysis/CalledOnceCheck.cpp +++ b/clang/lib/Analysis/CalledOnceCheck.cpp @@ -867,16 +867,14 @@ class CalledOnceChecker : public ConstStmtVisitor { // Let's check if any of the call arguments is a point of interest. for (const auto : llvm::enumerate(Arguments)) { if (auto Index = getIndexOfExpression(Argument.value())) { -ParameterStatus = CurrentState.getStatusFor(*Index); - if (shouldBeCalledOnce(CallOrMessage, Argument.index())) { // If the corresponding parameter is marked as 'called_once' we should // consider it as a call. processCallFor(*Index, CallOrMessage); -} else if (CurrentParamStatus.getKind() == ParameterStatus::NotCalled) { +} else { // Otherwise, we mark this parameter as escaped, which can be // interpreted both as called or not called depending on the context. - CurrentParamStatus = ParameterStatus::Escaped; + processEscapeFor(*Index); } // Otherwise, let's keep the state as it is. } @@ -910,6 +908,16 @@ class CalledOnceChecker : public ConstStmtVisitor { } } + /// Process escape of the parameter with the given index + void processEscapeFor(unsigned Index) { +ParameterStatus = CurrentState.getStatusFor(Index); + +// Escape overrides whatever error we think happened. +if (CurrentParamStatus.isErrorStatus()) { + CurrentParamStatus = ParameterStatus::Escaped; +} + } + void findAndReportNotCalledBranches(const CFGBlock *Parent, unsigned Index, bool IsEscape = false) { for (const CFGBlock *Succ : Parent->succs()) { @@ -1365,11 +1373,7 @@ class CalledOnceChecker : public ConstStmtVisitor { /// Check given parameter that was discovered to escape. void checkEscapee(const ParmVarDecl ) { if (auto Index = getIndex(Parameter)) { - ParameterStatus = CurrentState.getStatusFor(*Index); - - if (CurrentParamStatus.getKind() == ParameterStatus::NotCalled) { -CurrentParamStatus = ParameterStatus::Escaped; - } + processEscapeFor(*Index); } } diff --git a/clang/test/SemaObjC/warn-called-once.m b/clang/test/SemaObjC/warn-called-once.m index 3d846deca921..7d0679035238 100644 --- a/clang/test/SemaObjC/warn-called-once.m +++ b/clang/test/SemaObjC/warn-called-once.m @@ -1130,4 +1130,32 @@ - (void)test_nil_suppression_3:(int)cond1 } } +- (void)test_escape_before_branch:(int)cond + withCompletion:(void (^)(void))handler { + if (cond) { +filler(); + } + + void (^copiedHandler)(void) = ^{ +handler(); + }; + + if (cond) { +// no-warning +handler(); + } else { +copiedHandler(); + } +} + +- (void)test_escape_after_branch:(int)cond + withCompletion:(void (^)(void))handler { + if (cond) { +// no-warning +handler(); + } + + escape(handler); +} + @end ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 6dc1523 - [analyzer][solver] Prevent infeasible states (PR49490)
Author: Valeriy Savchenko Date: 2021-03-12T15:56:48+03:00 New Revision: 6dc152350824d0abcf4e1836c2846f8f9256779c URL: https://github.com/llvm/llvm-project/commit/6dc152350824d0abcf4e1836c2846f8f9256779c DIFF: https://github.com/llvm/llvm-project/commit/6dc152350824d0abcf4e1836c2846f8f9256779c.diff LOG: [analyzer][solver] Prevent infeasible states (PR49490) This patch fixes the situation when our knowledge of disequalities can help us figuring out that some assumption is infeasible, but the solver still produces a state with inconsistent constraints. Additionally, this patch adds a couple of assertions to catch this type of problems easier. Differential Revision: https://reviews.llvm.org/D98341 Added: clang/test/Analysis/PR49490.cpp Modified: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index a481bde1651b..95e61f9c8c61 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -19,6 +19,8 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -1395,12 +1397,23 @@ class RangeConstraintManager : public RangedConstraintManager { return EquivalenceClass::merge(getBasicVals(), F, State, LHS, RHS); } - LLVM_NODISCARD inline ProgramStateRef setConstraint(ProgramStateRef State, - EquivalenceClass Class, - RangeSet Constraint) { + LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED static bool + areFeasible(ConstraintRangeTy Constraints) { +return llvm::none_of( +Constraints, +[](const std::pair ) { + return ClassConstraint.second.isEmpty(); +}); + } + + LLVM_NODISCARD ProgramStateRef setConstraint(ProgramStateRef State, + EquivalenceClass Class, + RangeSet Constraint) { ConstraintRangeTy Constraints = State->get(); ConstraintRangeTy::Factory = State->get_context(); +assert(!Constraint.isEmpty() && "New constraint should not be empty"); + // Add new constraint. Constraints = CF.add(Constraints, Class, Constraint); @@ -1413,9 +1426,18 @@ class RangeConstraintManager : public RangedConstraintManager { for (EquivalenceClass DisequalClass : Class.getDisequalClasses(State)) { RangeSet UpdatedConstraint = getRange(State, DisequalClass).Delete(getBasicVals(), F, *Point); + +// If we end up with at least one of the disequal classes to be +// constrainted with an empty range-set, the state is infeasible. +if (UpdatedConstraint.isEmpty()) + return nullptr; + Constraints = CF.add(Constraints, DisequalClass, UpdatedConstraint); } +assert(areFeasible(Constraints) && "Constraint manager shouldn't produce " + "a state with infeasible constraints"); + return State->set(Constraints); } diff --git a/clang/test/Analysis/PR49490.cpp b/clang/test/Analysis/PR49490.cpp new file mode 100644 index ..3254355013a6 --- /dev/null +++ b/clang/test/Analysis/PR49490.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_analyze_cc1 -w -analyzer-checker=core -verify %s + +// expected-no-diagnostics + +struct toggle { + bool value; +}; + +toggle global_toggle; +toggle get_global_toggle() { return global_toggle; } + +int oob_access(); + +bool compare(toggle one, bool other) { + if (one.value != other) +return true; + + if (one.value) +oob_access(); + return true; +} + +bool coin(); + +void bar() { + bool left = coin(); + bool right = coin(); + for (;;) +compare(get_global_toggle(), left) && compare(get_global_toggle(), right); +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] c763504 - [analyzer] Fix StdLibraryFunctionsChecker performance issue
Author: Valeriy Savchenko Date: 2021-03-10T10:44:04+03:00 New Revision: c7635040ce0a546020cbcd9f2030817725bd7494 URL: https://github.com/llvm/llvm-project/commit/c7635040ce0a546020cbcd9f2030817725bd7494 DIFF: https://github.com/llvm/llvm-project/commit/c7635040ce0a546020cbcd9f2030817725bd7494.diff LOG: [analyzer] Fix StdLibraryFunctionsChecker performance issue `initFunctionSummaries` lazily initializes a data structure with function summaries for standard library functions. It is called for every pre-, post-, and eval-call events, i.e. 3 times for each call on the path. If the initialization doesn't find any standard library functions in the translation unit, it will get re-tried (with the same effect) many times even for small translation units. For projects not using standard libraries, the speed-up can reach 50% after this patch. Differential Revision: https://reviews.llvm.org/D98244 Added: Modified: clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp index d1c366a94fac..38a9d4ba65b6 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -508,6 +508,7 @@ class StdLibraryFunctionsChecker mutable FunctionSummaryMapType FunctionSummaryMap; mutable std::unique_ptr BT_InvalidArg; + mutable bool SummariesInitialized = false; static SVal getArgSVal(const CallEvent , ArgNo ArgN) { return ArgN == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgN); @@ -823,7 +824,7 @@ StdLibraryFunctionsChecker::findFunctionSummary(const CallEvent , void StdLibraryFunctionsChecker::initFunctionSummaries( CheckerContext ) const { - if (!FunctionSummaryMap.empty()) + if (SummariesInitialized) return; SValBuilder = C.getSValBuilder(); @@ -2485,6 +2486,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( Signature(ArgTypes{VoidPtrRestrictTy}, RetType{VoidTy}), Summary(EvalCallAsPure)); } + + SummariesInitialized = true; } void ento::registerStdCLibraryFunctionsChecker(CheckerManager ) { ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 59112ea - [-Wcompletion-handler] Extend list of detected conventions
Author: Valeriy Savchenko Date: 2021-03-10T10:43:19+03:00 New Revision: 59112eacb97910601504d4ce5115b2d535bcbeb6 URL: https://github.com/llvm/llvm-project/commit/59112eacb97910601504d4ce5115b2d535bcbeb6 DIFF: https://github.com/llvm/llvm-project/commit/59112eacb97910601504d4ce5115b2d535bcbeb6.diff LOG: [-Wcompletion-handler] Extend list of detected conventions Update convention detection to accomodate changes from: https://github.com/DougGregor/swift-evolution/blob/concurrency-objc/proposals/-concurrency-objc.md#asynchronous-completion-handler-methods Differential Revision: https://reviews.llvm.org/D98251 Added: Modified: clang/lib/Analysis/CalledOnceCheck.cpp clang/test/SemaObjC/warn-called-once.m Removed: diff --git a/clang/lib/Analysis/CalledOnceCheck.cpp b/clang/lib/Analysis/CalledOnceCheck.cpp index 92d68d85fbc2..2438c50d7e4e 100644 --- a/clang/lib/Analysis/CalledOnceCheck.cpp +++ b/clang/lib/Analysis/CalledOnceCheck.cpp @@ -48,9 +48,12 @@ static constexpr unsigned EXPECTED_NUMBER_OF_BASIC_BLOCKS = 8; template using CFGSizedVector = llvm::SmallVector; constexpr llvm::StringLiteral CONVENTIONAL_NAMES[] = { -"completionHandler", "completion", "withCompletionHandler"}; +"completionHandler", "completion", "withCompletionHandler", +"withCompletion","completionBlock", "withCompletionBlock", +"replyTo", "reply", "withReplyTo"}; constexpr llvm::StringLiteral CONVENTIONAL_SUFFIXES[] = { -"WithCompletionHandler", "WithCompletion"}; +"WithCompletionHandler", "WithCompletion", "WithCompletionBlock", +"WithReplyTo", "WithReply"}; constexpr llvm::StringLiteral CONVENTIONAL_CONDITIONS[] = { "error", "cancel", "shouldCall", "done", "OK", "success"}; @@ -994,13 +997,15 @@ class CalledOnceChecker : public ConstStmtVisitor { return hasConventionalSuffix(MethodSelector.getNameForSlot(0)); } -return isConventional(MethodSelector.getNameForSlot(PieceIndex)); +llvm::StringRef PieceName = MethodSelector.getNameForSlot(PieceIndex); +return isConventional(PieceName) || hasConventionalSuffix(PieceName); } bool shouldBeCalledOnce(const ParmVarDecl *Parameter) const { return isExplicitlyMarked(Parameter) || (CheckConventionalParameters && -isConventional(Parameter->getName()) && +(isConventional(Parameter->getName()) || + hasConventionalSuffix(Parameter->getName())) && isConventional(Parameter->getType())); } diff --git a/clang/test/SemaObjC/warn-called-once.m b/clang/test/SemaObjC/warn-called-once.m index 1014e1730163..3d846deca921 100644 --- a/clang/test/SemaObjC/warn-called-once.m +++ b/clang/test/SemaObjC/warn-called-once.m @@ -1036,6 +1036,20 @@ - (void)testWithCompletion:(void (^)(void))callback { } } +- (void)test:(int)cond fooWithReplyTo:(void (^)(void))handler { + if (cond) { +// expected-warning@-1{{completion handler is never called when taking false branch}} +handler(); + } +} + +- (void)test:(int)cond with:(void (^)(void))fooWithCompletionBlock { + if (cond) { +// expected-warning@-1{{completion handler is never called when taking false branch}} +fooWithCompletionBlock(); + } +} + - (void)completion_handler_wrong_type:(int (^)(void))completionHandler { // We don't want to consider completion handlers with non-void return types. if ([self condition]) { ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 6f21ada - [analyzer][NFC] Fix test failures for builds w/o assertions
Author: Valeriy Savchenko Date: 2021-02-15T16:38:15+03:00 New Revision: 6f21adac6dd7082f7231ae342d40ed04f4885e79 URL: https://github.com/llvm/llvm-project/commit/6f21adac6dd7082f7231ae342d40ed04f4885e79 DIFF: https://github.com/llvm/llvm-project/commit/6f21adac6dd7082f7231ae342d40ed04f4885e79.diff LOG: [analyzer][NFC] Fix test failures for builds w/o assertions Added: Modified: clang/test/Analysis/reinterpret-cast-pointer-to-member.cpp Removed: diff --git a/clang/test/Analysis/reinterpret-cast-pointer-to-member.cpp b/clang/test/Analysis/reinterpret-cast-pointer-to-member.cpp index 1631be70da3e..c457d2230ddd 100644 --- a/clang/test/Analysis/reinterpret-cast-pointer-to-member.cpp +++ b/clang/test/Analysis/reinterpret-cast-pointer-to-member.cpp @@ -1,5 +1,5 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s -// XFAIL: * +// XFAIL: asserts void clang_analyzer_eval(bool); ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] f8d3f47 - [analyzer] Updated comments to reflect D85817
Author: Deep Majumder Date: 2021-02-15T11:47:21+03:00 New Revision: f8d3f47e1fd09392aa30df83849b25acd8c59a25 URL: https://github.com/llvm/llvm-project/commit/f8d3f47e1fd09392aa30df83849b25acd8c59a25 DIFF: https://github.com/llvm/llvm-project/commit/f8d3f47e1fd09392aa30df83849b25acd8c59a25.diff LOG: [analyzer] Updated comments to reflect D85817 Changed DeclaratorDecl in comment to NamedDecl Reviewed By: vsavchenko Differential Revision: https://reviews.llvm.org/D95846 Added: Modified: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h index a2354ba62cdb..b1c33713febd 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -511,11 +511,11 @@ class LazyCompoundVal : public NonLoc { /// This value is qualified as NonLoc because neither loading nor storing /// operations are applied to it. Instead, the analyzer uses the L-value coming /// from pointer-to-member applied to an object. -/// This SVal is represented by a DeclaratorDecl which can be a member function -/// pointer or a member data pointer and a list of CXXBaseSpecifiers. This list -/// is required to accumulate the pointer-to-member cast history to figure out -/// the correct subobject field. In particular, implicit casts grow this list -/// and explicit casts like static_cast shrink this list. +/// This SVal is represented by a NamedDecl which can be a member function +/// pointer or a member data pointer and an optional list of CXXBaseSpecifiers. +/// This list is required to accumulate the pointer-to-member cast history to +/// figure out the correct subobject field. In particular, implicit casts grow +/// this list and explicit casts like static_cast shrink this list. class PointerToMember : public NonLoc { friend class ento::SValBuilder; ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 21daada - [analyzer] Fix static_cast on pointer-to-member handling
Author: Deep Majumder Date: 2021-02-15T11:44:37+03:00 New Revision: 21daada95079a37c7ca259fabfc735b6d1b362ad URL: https://github.com/llvm/llvm-project/commit/21daada95079a37c7ca259fabfc735b6d1b362ad DIFF: https://github.com/llvm/llvm-project/commit/21daada95079a37c7ca259fabfc735b6d1b362ad.diff LOG: [analyzer] Fix static_cast on pointer-to-member handling This commit fixes bug #48739. The bug was caused by the way static_casts on pointer-to-member caused the CXXBaseSpecifier list of a MemberToPointer to grow instead of shrink. The list is now grown by implicit casts and corresponding entries are removed by static_casts. No-op static_casts cause no effect. Reviewed By: vsavchenko Differential Revision: https://reviews.llvm.org/D95877 Added: clang/test/Analysis/reinterpret-cast-pointer-to-member.cpp Modified: clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp clang/test/Analysis/pointer-to-member.cpp Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h index 142b1ab11750..9f464e82304f 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h @@ -258,9 +258,9 @@ class BasicValueFactory { return CXXBaseListFactory.add(CBS, L); } - const PointerToMemberData *accumCXXBase( - llvm::iterator_range PathRange, - const nonloc::PointerToMember ); + const PointerToMemberData * + accumCXXBase(llvm::iterator_range PathRange, + const nonloc::PointerToMember , const clang::CastKind ); const llvm::APSInt* evalAPSInt(BinaryOperator::Opcode Op, const llvm::APSInt& V1, diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h index bb295ab591d4..a2354ba62cdb 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -514,7 +514,8 @@ class LazyCompoundVal : public NonLoc { /// This SVal is represented by a DeclaratorDecl which can be a member function /// pointer or a member data pointer and a list of CXXBaseSpecifiers. This list /// is required to accumulate the pointer-to-member cast history to figure out -/// the correct subobject field. +/// the correct subobject field. In particular, implicit casts grow this list +/// and explicit casts like static_cast shrink this list. class PointerToMember : public NonLoc { friend class ento::SValBuilder; diff --git a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index d1f5ac02278f..40cdaef1bfa7 100644 --- a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -21,6 +21,7 @@ #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" #include #include #include @@ -176,28 +177,73 @@ const PointerToMemberData *BasicValueFactory::getPointerToMemberData( return D; } +LLVM_ATTRIBUTE_UNUSED bool hasNoRepeatedElements( +llvm::ImmutableList BaseSpecList) { + llvm::SmallPtrSet BaseSpecSeen; + for (const CXXBaseSpecifier *BaseSpec : BaseSpecList) { +QualType BaseType = BaseSpec->getType(); +// Check whether inserted +if (!BaseSpecSeen.insert(BaseType).second) + return false; + } + return true; +} + const PointerToMemberData *BasicValueFactory::accumCXXBase( llvm::iterator_range PathRange, -const nonloc::PointerToMember ) { +const nonloc::PointerToMember , const CastKind ) { + assert((kind == CK_DerivedToBaseMemberPointer || + kind == CK_BaseToDerivedMemberPointer || + kind == CK_ReinterpretMemberPointer) && + "accumCXXBase called with wrong CastKind"); nonloc::PointerToMember::PTMDataType PTMDT = PTM.getPTMData(); const NamedDecl *ND = nullptr; - llvm::ImmutableList PathList; + llvm::ImmutableList BaseSpecList; if (PTMDT.isNull() || PTMDT.is()) { if (PTMDT.is()) ND = PTMDT.get(); -PathList = CXXBaseListFactory.getEmptyList(); - } else { // const PointerToMemberData * +BaseSpecList = CXXBaseListFactory.getEmptyList(); + } else { const PointerToMemberData *PTMD = PTMDT.get(); ND = PTMD->getDeclaratorDecl(); -PathList = PTMD->getCXXBaseList(); +BaseSpecList = PTMD->getCXXBaseList(); } - for (const auto : llvm::reverse(PathRange)) -PathList = prependCXXBase(I, PathList); -
[clang] 94a1a5d - [analyzer][tests] Fix issue comparison script
Author: Valeriy Savchenko Date: 2021-02-13T13:58:47+03:00 New Revision: 94a1a5d25f5546019fc5feeb24d924b09b9d27e4 URL: https://github.com/llvm/llvm-project/commit/94a1a5d25f5546019fc5feeb24d924b09b9d27e4 DIFF: https://github.com/llvm/llvm-project/commit/94a1a5d25f5546019fc5feeb24d924b09b9d27e4.diff LOG: [analyzer][tests] Fix issue comparison script When newer build has duplicate issues the script tried to remove it from the list more than once. The new approach changes the way we filter out matching issues. Differential Revision: https://reviews.llvm.org/D96611 Added: Modified: clang/utils/analyzer/CmpRuns.py Removed: diff --git a/clang/utils/analyzer/CmpRuns.py b/clang/utils/analyzer/CmpRuns.py index 9d5e00767067..7afe865d77f2 100644 --- a/clang/utils/analyzer/CmpRuns.py +++ b/clang/utils/analyzer/CmpRuns.py @@ -36,7 +36,7 @@ from copy import copy from enum import Enum from typing import (Any, DefaultDict, Dict, List, NamedTuple, Optional, -Sequence, TextIO, TypeVar, Tuple, Union) +Sequence, Set, TextIO, TypeVar, Tuple, Union) Number = Union[int, float] @@ -374,8 +374,9 @@ def compare_results(results_old: AnalysisRun, results_new: AnalysisRun, # Quadratic algorithms in this part are fine because 'old' and 'new' # are most commonly of size 1. -for a in copy(old): -for b in copy(new): +common: Set[AnalysisDiagnostic] = set() +for a in old: +for b in new: if a.get_issue_identifier() == b.get_issue_identifier(): a_path_len = a.get_path_length() b_path_len = b.get_path_length() @@ -394,16 +395,22 @@ def compare_results(results_old: AnalysisRun, results_new: AnalysisRun, path_ diff erence_data.append( a_path_len - b_path_len) -res.add_common(a) -old.remove(a) -new.remove(b) +res.add_common(b) +common.add(a) + +old = filter_issues(old, common) +new = filter_issues(new, common) +common = set() -for a in copy(old): -for b in copy(new): +for a in old: +for b in new: if a.is_similar_to(b): res.add_changed(a, b) -old.remove(a) -new.remove(b) +common.add(a) +common.add(b) + +old = filter_issues(old, common) +new = filter_issues(new, common) # Whatever is left in 'old' doesn't have a corresponding diagnostic # in 'new', so we need to mark it as 'removed'. @@ -443,6 +450,12 @@ def compare_results(results_old: AnalysisRun, results_new: AnalysisRun, return res +def filter_issues(origin: List[AnalysisDiagnostic], + to_remove: Set[AnalysisDiagnostic]) \ + -> List[AnalysisDiagnostic]: +return [diag for diag in origin if diag not in to_remove] + + def compute_percentile(values: Sequence[T], percentile: float) -> T: """ Return computed percentile. ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 81a9707 - [Attr] Apply GNU-style attributes to expression statements
Author: Valeriy Savchenko Date: 2021-02-11T16:44:41+03:00 New Revision: 81a9707723845a5b880245da24633c519493205c URL: https://github.com/llvm/llvm-project/commit/81a9707723845a5b880245da24633c519493205c DIFF: https://github.com/llvm/llvm-project/commit/81a9707723845a5b880245da24633c519493205c.diff LOG: [Attr] Apply GNU-style attributes to expression statements Before this commit, expression statements could not be annotated with statement attributes. Whenever parser found attribute, it unconditionally assumed that it was followed by a declaration. This not only doesn't allow expression attributes to have attributes, but also produces spurious error diagnostics. In order to maintain all previously compiled code, we still assume that GNU attributes are followed by declarations unless ALL of those are statement attributes. And even in this case we are not forcing the parser to think that it should parse a statement, but rather let it proceed as if no attributes were found. Differential Revision: https://reviews.llvm.org/D93630 Added: clang/test/Parser/stmt-attributes.c clang/test/Parser/stmt-attributes.cpp clang/test/Parser/stmt-attributes.m Modified: clang/include/clang/Basic/Features.def clang/lib/Parse/ParseStmt.cpp Removed: diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 5424da67b62da..32830a3a532cc 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -253,6 +253,7 @@ EXTENSION(cxx_variable_templates, LangOpts.CPlusPlus) EXTENSION(overloadable_unmarked, true) EXTENSION(pragma_clang_attribute_namespaces, true) EXTENSION(pragma_clang_attribute_external_declaration, true) +EXTENSION(statement_attributes_with_gnu_syntax, true) EXTENSION(gnu_asm, LangOpts.GNUAsm) EXTENSION(gnu_asm_goto_with_outputs, LangOpts.GNUAsm) EXTENSION(matrix_types, LangOpts.MatrixTypes) diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 71344ff101552..f59271c458488 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -20,6 +20,8 @@ #include "clang/Sema/DeclSpec.h" #include "clang/Sema/Scope.h" #include "clang/Sema/TypoCorrection.h" +#include "llvm/ADT/STLExtras.h" + using namespace clang; //===--===// @@ -215,7 +217,11 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes( if ((getLangOpts().CPlusPlus || getLangOpts().MicrosoftExt || (StmtCtx & ParsedStmtContext::AllowDeclarationsInC) != ParsedStmtContext()) && -(GNUAttributeLoc.isValid() || isDeclarationStatement())) { +((GNUAttributeLoc.isValid() && + !(!Attrs.empty() && +llvm::all_of( +Attrs, [](ParsedAttr ) { return Attr.isStmtAttr(); }))) || + isDeclarationStatement())) { SourceLocation DeclStart = Tok.getLocation(), DeclEnd; DeclGroupPtrTy Decl; if (GNUAttributeLoc.isValid()) { diff --git a/clang/test/Parser/stmt-attributes.c b/clang/test/Parser/stmt-attributes.c new file mode 100644 index 0..d142ce1b5b954 --- /dev/null +++ b/clang/test/Parser/stmt-attributes.c @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#if !__has_extension(statement_attributes_with_gnu_syntax) +#error "We should have statement attributes with GNU syntax support" +#endif + +void foo(int i) { + + __attribute__((unknown_attribute)); // expected-warning {{unknown attribute 'unknown_attribute' ignored}} + __attribute__(()) {} + __attribute__(()) if (0) {} + __attribute__(()) for (;;); + __attribute__(()) do { +__attribute__(()) continue; + } + while (0) +; + __attribute__(()) while (0); + + __attribute__(()) switch (i) { +__attribute__(()) case 0 : +__attribute__(()) default : +__attribute__(()) break; + } + + __attribute__(()) goto here; + __attribute__(()) here : + + __attribute__(()) return; + + __attribute__((noreturn)) {} // expected-error {{'noreturn' attribute cannot be applied to a statement}} + __attribute__((noreturn)) if (0) {} // expected-error {{'noreturn' attribute cannot be applied to a statement}} + __attribute__((noreturn)) for (;;); // expected-error {{'noreturn' attribute cannot be applied to a statement}} + __attribute__((noreturn)) do { // expected-error {{'noreturn' attribute cannot be applied to a statement}} +__attribute__((unavailable)) continue; // expected-error {{'unavailable' attribute cannot be applied to a statement}} + } + while (0) +; + __attribute__((unknown_attribute)) while (0); // expected-warning {{unknown attribute 'unknown_attribute' ignored}} + + __attribute__((unused)) switch (i) { // expected-error {{'unused' attribute cannot be applied to a statement}} +
[clang] 2f994d4 - [-Wcompletion-handler][NFC] Remove unexpected warnings on Windows
Author: Valeriy Savchenko Date: 2021-02-09T13:50:11+03:00 New Revision: 2f994d4ee920983cf7624ce2208756e0c7d19007 URL: https://github.com/llvm/llvm-project/commit/2f994d4ee920983cf7624ce2208756e0c7d19007 DIFF: https://github.com/llvm/llvm-project/commit/2f994d4ee920983cf7624ce2208756e0c7d19007.diff LOG: [-Wcompletion-handler][NFC] Remove unexpected warnings on Windows Added: Modified: clang/test/SemaObjC/warn-called-once.m Removed: diff --git a/clang/test/SemaObjC/warn-called-once.m b/clang/test/SemaObjC/warn-called-once.m index 0c11d0e0a15b..1014e1730163 100644 --- a/clang/test/SemaObjC/warn-called-once.m +++ b/clang/test/SemaObjC/warn-called-once.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -verify -fsyntax-only -fblocks -fobjc-exceptions -Wcompletion-handler %s +// RUN: %clang_cc1 -verify -fsyntax-only -fblocks -fobjc-exceptions -Wcompletion-handler -Wno-pointer-to-int-cast %s #define NULL (void *)0 #define nil (id)0 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] d1522d3 - [-Wcompletion-handler] Support checks with builtins
Author: Valeriy Savchenko Date: 2021-02-09T11:32:24+03:00 New Revision: d1522d349f4d4b960ff7a37303103e95aa535af3 URL: https://github.com/llvm/llvm-project/commit/d1522d349f4d4b960ff7a37303103e95aa535af3 DIFF: https://github.com/llvm/llvm-project/commit/d1522d349f4d4b960ff7a37303103e95aa535af3.diff LOG: [-Wcompletion-handler] Support checks with builtins It is very common to check callbacks and completion handlers for null. This patch supports such checks using built-in functions: * __builtin_expect * __builtin_expect_with_probablity * __builtin_unpredictable rdar://73455388 Differential Revision: https://reviews.llvm.org/D96268 Added: Modified: clang/lib/Analysis/CalledOnceCheck.cpp clang/test/SemaObjC/warn-called-once.m Removed: diff --git a/clang/lib/Analysis/CalledOnceCheck.cpp b/clang/lib/Analysis/CalledOnceCheck.cpp index 883629a300dc..92d68d85fbc2 100644 --- a/clang/lib/Analysis/CalledOnceCheck.cpp +++ b/clang/lib/Analysis/CalledOnceCheck.cpp @@ -22,6 +22,7 @@ #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/FlowSensitive/DataflowWorklist.h" +#include "clang/Basic/Builtins.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/BitVector.h" @@ -330,6 +331,29 @@ class DeclRefFinder return Visit(OVE->getSourceExpr()); } + const DeclRefExpr *VisitCallExpr(const CallExpr *CE) { +if (!ShouldRetrieveFromComparisons) + return nullptr; + +// We want to see through some of the boolean builtin functions +// that we are likely to see in conditions. +switch (CE->getBuiltinCallee()) { +case Builtin::BI__builtin_expect: +case Builtin::BI__builtin_expect_with_probability: { + assert(CE->getNumArgs() >= 2); + + const DeclRefExpr *Candidate = Visit(CE->getArg(0)); + return Candidate != nullptr ? Candidate : Visit(CE->getArg(1)); +} + +case Builtin::BI__builtin_unpredictable: + return Visit(CE->getArg(0)); + +default: + return nullptr; +} + } + const DeclRefExpr *VisitExpr(const Expr *E) { // It is a fallback method that gets called whenever the actual type // of the given expression is not covered. diff --git a/clang/test/SemaObjC/warn-called-once.m b/clang/test/SemaObjC/warn-called-once.m index 094f92a49935..0c11d0e0a15b 100644 --- a/clang/test/SemaObjC/warn-called-once.m +++ b/clang/test/SemaObjC/warn-called-once.m @@ -4,6 +4,11 @@ #define nil (id)0 #define CALLED_ONCE __attribute__((called_once)) #define NORETURN __attribute__((noreturn)) +#define LIKELY(X) __builtin_expect(!!(X), 1) +#define UNLIKELY(X) __builtin_expect(!!(X), 0) +#define LIKELY_WITH_PROBA(X, P) __builtin_expect_with_probability(!!(X), 1, P) +#define UNLIKELY_WITH_PROBA(X, P) __builtin_expect_with_probability(!!(X), 0, P) +#define UNPRED(X) __builtin_unpredictable((long)(X)) @protocol NSObject @end @@ -547,6 +552,70 @@ int call_with_check_7(int (^callback)(void) CALLED_ONCE) { // no-warning } +void call_with_builtin_check_1(int (^callback)(void) CALLED_ONCE) { + if (LIKELY(callback)) +callback(); + // no-warning +} + +void call_with_builtin_check_2(int (^callback)(void) CALLED_ONCE) { + if (!UNLIKELY(callback)) { + } else { +callback(); + } + // no-warning +} + +void call_with_builtin_check_3(int (^callback)(void) CALLED_ONCE) { + if (__builtin_expect((long)callback, 0L)) { + } else { +callback(); + } + // no-warning +} + +void call_with_builtin_check_4(int (^callback)(void) CALLED_ONCE) { + if (__builtin_expect(0L, (long)callback)) { + } else { +callback(); + } + // no-warning +} + +void call_with_builtin_check_5(int (^callback)(void) CALLED_ONCE) { + if (LIKELY_WITH_PROBA(callback, 0.9)) +callback(); + // no-warning +} + +void call_with_builtin_check_6(int (^callback)(void) CALLED_ONCE) { + if (!UNLIKELY_WITH_PROBA(callback, 0.9)) { + } else { +callback(); + } + // no-warning +} + +void call_with_builtin_check_7(int (^callback)(void) CALLED_ONCE) { + if (UNPRED(callback)) { + } else { +callback(); + } + // no-warning +} + +void call_with_builtin_check_8(int (^callback)(void) CALLED_ONCE) { + if (LIKELY(callback != nil)) +callback(); + // no-warning +} + +void call_with_builtin_check_9(int (^callback)(void) CALLED_ONCE) { + if (!UNLIKELY(callback == NULL)) +callback(); + // no-warning +} + void unreachable_true_branch(void (^callback)(void) CALLED_ONCE) { if (0) { ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 9300ca5 - [doxygen] Fix bad doxygen results for BugReporterVisitors.h
Author: Ella Ma Date: 2020-08-28T12:41:16+03:00 New Revision: 9300ca541164b80efb7d42dc45219b5d8e05de68 URL: https://github.com/llvm/llvm-project/commit/9300ca541164b80efb7d42dc45219b5d8e05de68 DIFF: https://github.com/llvm/llvm-project/commit/9300ca541164b80efb7d42dc45219b5d8e05de68.diff LOG: [doxygen] Fix bad doxygen results for BugReporterVisitors.h `{@code x}` triggers a Doxygen bug. The bug may be matching the close brace with the open brace of the namespace declaration (`namespace clang {` or `namespace ento {`). Differential Revision: https://reviews.llvm.org/D85105 Added: Modified: clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h index 1aff2ca34a70..58a88f452ed9 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h @@ -53,7 +53,7 @@ class BugReporterVisitor : public llvm::FoldingSetNode { /// Note that this function does *not* get run on the very last node /// of the report, as the PathDiagnosticPiece associated with the /// last node should be unique. - /// Use {@code getEndPath} to customize the note associated with the report + /// Use \ref getEndPath to customize the note associated with the report /// end instead. /// /// The last parameter can be used to register a new visitor with the given ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] aec12c1 - [analyzer][tests] Add a notion of project sizes
Author: Valeriy Savchenko Date: 2020-08-24T16:13:00+03:00 New Revision: aec12c1264ac17877d5cb19750eaa322fe57342d URL: https://github.com/llvm/llvm-project/commit/aec12c1264ac17877d5cb19750eaa322fe57342d DIFF: https://github.com/llvm/llvm-project/commit/aec12c1264ac17877d5cb19750eaa322fe57342d.diff LOG: [analyzer][tests] Add a notion of project sizes Summary: Whith the number of projects growing, it is important to be able to filter them in a more convenient way than by names. It is especially important for benchmarks, when it is not viable to analyze big projects 20 or 50 times in a row. Because of this reason, this commit adds a notion of sizes and a filtering interface that puts a limit on a maximum size of the project to analyze or benchmark. Sizes assigned to the projects in this commit, do not directly correspond to the number of lines or files in the project. The key factor that is important for the developers of the analyzer is the time it takes to analyze the project. And for this very reason, "size" basically helps to cluster projects based on their analysis time. Differential Revision: https://reviews.llvm.org/D83942 Added: Modified: clang/utils/analyzer/ProjectMap.py clang/utils/analyzer/SATest.py clang/utils/analyzer/projects/projects.json Removed: diff --git a/clang/utils/analyzer/ProjectMap.py b/clang/utils/analyzer/ProjectMap.py index 3daa70140562..1e89ce634e57 100644 --- a/clang/utils/analyzer/ProjectMap.py +++ b/clang/utils/analyzer/ProjectMap.py @@ -1,7 +1,7 @@ import json import os -from enum import Enum +from enum import auto, Enum from typing import Any, Dict, List, NamedTuple, Optional, Tuple @@ -17,6 +17,64 @@ class DownloadType(str, Enum): SCRIPT = "script" +class Size(int, Enum): +""" +Size of the project. + +Sizes do not directly correspond to the number of lines or files in the +project. The key factor that is important for the developers of the +analyzer is the time it takes to analyze the project. Here is how +the following sizes map to times: + +TINY: <1min +SMALL: 1min-10min +BIG: 10min-1h +HUGE: >1h + +The borders are a bit of a blur, especially because analysis time varies +from one machine to another. However, the relative times will stay pretty +similar, and these groupings will still be helpful. + +UNSPECIFIED is a very special case, which is intentionally last in the list +of possible sizes. If the user wants to filter projects by one of the +possible sizes, we want projects with UNSPECIFIED size to be filtered out +for any given size. +""" +TINY = auto() +SMALL = auto() +BIG = auto() +HUGE = auto() +UNSPECIFIED = auto() + +@staticmethod +def from_str(raw_size: Optional[str]) -> "Size": +""" +Construct a Size object from an optional string. + +:param raw_size: optional string representation of the desired Size + object. None will produce UNSPECIFIED size. + +This method is case-insensitive, so raw sizes 'tiny', 'TINY', and +'TiNy' will produce the same result. +""" +if raw_size is None: +return Size.UNSPECIFIED + +raw_size_upper = raw_size.upper() +# The implementation is decoupled from the actual values of the enum, +# so we can easily add or modify it without bothering about this +# function. +for possible_size in Size: +if possible_size.name == raw_size_upper: +return possible_size + +possible_sizes = [size.name.lower() for size in Size + # no need in showing our users this size + if size != Size.UNSPECIFIED] +raise ValueError(f"Incorrect project size '{raw_size}'. " + f"Available sizes are {possible_sizes}") + + class ProjectInfo(NamedTuple): """ Information about a project to analyze. @@ -27,6 +85,7 @@ class ProjectInfo(NamedTuple): origin: str = "" commit: str = "" enabled: bool = True +size: Size = Size.UNSPECIFIED def with_fields(self, **kwargs) -> "ProjectInfo": """ @@ -98,6 +157,7 @@ def _parse_project(raw_project: JSON) -> ProjectInfo: build_mode: int = raw_project["mode"] enabled: bool = raw_project.get("enabled", True) source: DownloadType = raw_project.get("source", "zip") +size = Size.from_str(raw_project.get("size", None)) if source == DownloadType.GIT: origin, commit = ProjectMap._get_git_params(raw_project) @@ -105,7 +165,7 @@ def _parse_project(raw_project: JSON) -> ProjectInfo: origin, commit = "", "" return ProjectInfo(name, build_mode, source, origin, commit, -
[clang] 9cbfdde - [analyzer] Fix crash with pointer to members values
Author: Valeriy Savchenko Date: 2020-08-13T18:03:59+03:00 New Revision: 9cbfdde2ea060d7e51fd2637f63eaa74b8d92848 URL: https://github.com/llvm/llvm-project/commit/9cbfdde2ea060d7e51fd2637f63eaa74b8d92848 DIFF: https://github.com/llvm/llvm-project/commit/9cbfdde2ea060d7e51fd2637f63eaa74b8d92848.diff LOG: [analyzer] Fix crash with pointer to members values This fix unifies all of the different ways we handled pointer to members into one. The crash was caused by the fact that the type of pointer-to-member values was `void *`, and while this works for the vast majority of cases it breaks when we actually need to explain the path for the report. rdar://problem/64202361 Differential Revision: https://reviews.llvm.org/D85817 Added: clang/test/Analysis/PR46264.cpp Modified: clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp clang/lib/StaticAnalyzer/Core/ExprEngine.cpp clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp clang/lib/StaticAnalyzer/Core/SValBuilder.cpp clang/lib/StaticAnalyzer/Core/SVals.cpp clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp clang/test/Analysis/pointer-to-member.cpp Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h index a001c0dc7030..142b1ab11750 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h @@ -79,11 +79,11 @@ class LazyCompoundValData : public llvm::FoldingSetNode { }; class PointerToMemberData : public llvm::FoldingSetNode { - const DeclaratorDecl *D; + const NamedDecl *D; llvm::ImmutableList L; public: - PointerToMemberData(const DeclaratorDecl *D, + PointerToMemberData(const NamedDecl *D, llvm::ImmutableList L) : D(D), L(L) {} @@ -92,11 +92,11 @@ class PointerToMemberData : public llvm::FoldingSetNode { iterator begin() const { return L.begin(); } iterator end() const { return L.end(); } - static void Profile(llvm::FoldingSetNodeID& ID, const DeclaratorDecl *D, + static void Profile(llvm::FoldingSetNodeID , const NamedDecl *D, llvm::ImmutableList L); - void Profile(llvm::FoldingSetNodeID& ID) { Profile(ID, D, L); } - const DeclaratorDecl *getDeclaratorDecl() const {return D;} + void Profile(llvm::FoldingSetNodeID ) { Profile(ID, D, L); } + const NamedDecl *getDeclaratorDecl() const { return D; } llvm::ImmutableList getCXXBaseList() const { return L; @@ -236,9 +236,9 @@ class BasicValueFactory { const LazyCompoundValData *getLazyCompoundValData(const StoreRef , const TypedValueRegion *region); - const PointerToMemberData *getPointerToMemberData( - const DeclaratorDecl *DD, - llvm::ImmutableList L); + const PointerToMemberData * + getPointerToMemberData(const NamedDecl *ND, + llvm::ImmutableList L); llvm::ImmutableList getEmptySValList() { return SValListFactory.getEmptyList(); diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h index 35ebefdc00d6..4ea85f9730bb 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -233,7 +233,7 @@ class SValBuilder { const LocationContext *LCtx, unsigned count); - DefinedSVal getMemberPointer(const DeclaratorDecl *DD); + DefinedSVal getMemberPointer(const NamedDecl *ND); DefinedSVal getFunctionPointer(const FunctionDecl *func); diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h index a561ac67bf78..bb295ab591d4 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -520,7 +520,7 @@ class PointerToMember : public NonLoc { public: using PTMDataType = - llvm::PointerUnion; + llvm::PointerUnion; const PTMDataType getPTMData() const { return PTMDataType::getFromOpaqueValue(const_cast(Data)); @@ -528,7 +528,7 @@ class PointerToMember : public NonLoc { bool isNullMemberPointer() const; - const DeclaratorDecl *getDecl() const; + const NamedDecl *getDecl() const; template const AdjustedDecl *getDeclAs() const { diff --git a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
[clang] 6ddef92 - [analyzer][tests] Understand when diagnostics change between builds
Author: Valeriy Savchenko Date: 2020-08-06T12:53:20+03:00 New Revision: 6ddef92474583ef3c183da9bdc8c8e81ec578fd8 URL: https://github.com/llvm/llvm-project/commit/6ddef92474583ef3c183da9bdc8c8e81ec578fd8 DIFF: https://github.com/llvm/llvm-project/commit/6ddef92474583ef3c183da9bdc8c8e81ec578fd8.diff LOG: [analyzer][tests] Understand when diagnostics change between builds Before the patch `SATest compare`, produced quite obscure results when something about the diagnostic have changed (i.e. its description or the name of the corresponding checker) because it was simply two lists of warnings, ADDED and REMOVED. It was up to the developer to match those warnings, understand that they are essentially the same, and figure out what caused the difference. This patch introduces another category of results: MODIFIED. It tries to match new warnings against the old ones and prints out clues on what is different between two builds. Differential Revision: https://reviews.llvm.org/D85311 Added: Modified: clang/utils/analyzer/CmpRuns.py Removed: diff --git a/clang/utils/analyzer/CmpRuns.py b/clang/utils/analyzer/CmpRuns.py index f7f28d9dc72e..9d5e00767067 100644 --- a/clang/utils/analyzer/CmpRuns.py +++ b/clang/utils/analyzer/CmpRuns.py @@ -35,14 +35,16 @@ from collections import defaultdict from copy import copy from enum import Enum -from typing import (Any, cast, Dict, List, NamedTuple, Optional, Sequence, -TextIO, TypeVar, Tuple, Union) +from typing import (Any, DefaultDict, Dict, List, NamedTuple, Optional, +Sequence, TextIO, TypeVar, Tuple, Union) Number = Union[int, float] Stats = Dict[str, Dict[str, Number]] Plist = Dict[str, Any] JSON = Dict[str, Any] +# Diff in a form: field -> (before, after) +JSONDiff = Dict[str, Tuple[str, str]] # Type for generics T = TypeVar('T') @@ -136,6 +138,9 @@ def get_category(self) -> str: def get_description(self) -> str: return self._data['description'] +def get_location(self) -> str: +return f"{self.get_file_name()}:{self.get_line()}:{self.get_column()}" + def get_issue_identifier(self) -> str: id = self.get_file_name() + "+" @@ -172,11 +177,32 @@ def get_readable_name(self) -> str: return f"{file_prefix}{funcname_postfix}:{line}:{col}" \ f", {self.get_category()}: {self.get_description()}" +KEY_FIELDS = ["check_name", "category", "description"] + +def is_similar_to(self, other: "AnalysisDiagnostic") -> bool: +# We consider two diagnostics similar only if at least one +# of the key fields is the same in both diagnostics. +return len(self.get_ diff s(other)) != len(self.KEY_FIELDS) + +def get_ diff s(self, other: "AnalysisDiagnostic") -> JSONDiff: +return {field: (self._data[field], other._data[field]) +for field in self.KEY_FIELDS +if self._data[field] != other._data[field]} + # Note, the data format is not an API and may change from one analyzer # version to another. def get_raw_data(self) -> Plist: return self._data +def __eq__(self, other: object) -> bool: +return hash(self) == hash(other) + +def __ne__(self, other: object) -> bool: +return hash(self) != hash(other) + +def __hash__(self) -> int: +return hash(self.get_issue_identifier()) + class AnalysisRun: def __init__(self, info: SingleRunInfo): @@ -283,12 +309,39 @@ def cmp_analysis_diagnostic(d): return d.get_issue_identifier() -PresentInBoth = Tuple[AnalysisDiagnostic, AnalysisDiagnostic] -PresentOnlyInOld = Tuple[AnalysisDiagnostic, None] -PresentOnlyInNew = Tuple[None, AnalysisDiagnostic] -ComparisonResult = List[Union[PresentInBoth, - PresentOnlyInOld, - PresentOnlyInNew]] +AnalysisDiagnosticPair = Tuple[AnalysisDiagnostic, AnalysisDiagnostic] + + +class ComparisonResult: +def __init__(self): +self.present_in_both: List[AnalysisDiagnostic] = [] +self.present_only_in_old: List[AnalysisDiagnostic] = [] +self.present_only_in_new: List[AnalysisDiagnostic] = [] +self.changed_between_new_and_old: List[AnalysisDiagnosticPair] = [] + +def add_common(self, issue: AnalysisDiagnostic): +self.present_in_both.append(issue) + +def add_removed(self, issue: AnalysisDiagnostic): +self.present_only_in_old.append(issue) + +def add_added(self, issue: AnalysisDiagnostic): +self.present_only_in_new.append(issue) + +def add_changed(self, old_issue: AnalysisDiagnostic, +new_issue: AnalysisDiagnostic): +self.changed_between_new_and_old.append((old_issue, new_issue)) + + +GroupedDiagnostics = DefaultDict[str, List[AnalysisDiagnostic]] + + +def get_grouped_diagnostics(diagnostics:
[clang] 10851f9 - [analyzer][tests] Fix SATest update functionality
Author: Valeriy Savchenko Date: 2020-08-03T18:21:15+03:00 New Revision: 10851f9db5f7d163135374b8dfc945e1b4a9c7d6 URL: https://github.com/llvm/llvm-project/commit/10851f9db5f7d163135374b8dfc945e1b4a9c7d6 DIFF: https://github.com/llvm/llvm-project/commit/10851f9db5f7d163135374b8dfc945e1b4a9c7d6.diff LOG: [analyzer][tests] Fix SATest update functionality Summary: Not all projects in the project map file might have newer results for updating, we should handle this situation gracefully. Additionally, not every user of the test system would want storing reference results in git. For this reason, git functionality is now optional. Differential Revision: https://reviews.llvm.org/D84303 Added: Modified: clang/utils/analyzer/SATest.py clang/utils/analyzer/SATestUpdateDiffs.py Removed: diff --git a/clang/utils/analyzer/SATest.py b/clang/utils/analyzer/SATest.py index 46e636ad2895..86571902502f 100755 --- a/clang/utils/analyzer/SATest.py +++ b/clang/utils/analyzer/SATest.py @@ -78,7 +78,7 @@ def update(parser, args): project_map = ProjectMap() for project in project_map.projects: -SATestUpdateDiffs.update_reference_results(project) +SATestUpdateDiffs.update_reference_results(project, args.git) def benchmark(parser, args): @@ -277,7 +277,8 @@ def main(): "update", help="Update static analyzer reference results based on the previous " "run of SATest build. Assumes that SATest build was just run.") -# TODO: add option to decide whether we should use git +upd_parser.add_argument("--git", action="store_true", +help="Stage updated results using git.") upd_parser.set_defaults(func=update) # docker subcommand diff --git a/clang/utils/analyzer/SATestUpdateDiffs.py b/clang/utils/analyzer/SATestUpdateDiffs.py index 920fa15e4c6f..69b3383beaf1 100644 --- a/clang/utils/analyzer/SATestUpdateDiffs.py +++ b/clang/utils/analyzer/SATestUpdateDiffs.py @@ -15,7 +15,7 @@ Verbose = 0 -def update_reference_results(project: ProjectInfo): +def update_reference_results(project: ProjectInfo, git: bool = False): test_info = SATestBuild.TestInfo(project) tester = SATestBuild.ProjectTester(test_info) project_dir = tester.get_project_dir() @@ -27,9 +27,10 @@ def update_reference_results(project: ProjectInfo): created_results_path = tester.get_output_dir() if not os.path.exists(created_results_path): -print("New results not found, was SATestBuild.py previously run?", +print(f"Skipping project '{project.name}', " + f"it doesn't have newer results.", file=sys.stderr) -sys.exit(1) +return build_log_path = SATestBuild.get_build_log_path(ref_results_path) build_log_dir = os.path.dirname(os.path.abspath(build_log_path)) @@ -45,7 +46,8 @@ def run_cmd(command: str): # Remove reference results: in git, and then again for a good measure # with rm, as git might not remove things fully if there are empty # directories involved. -run_cmd(f"git rm -r -q '{ref_results_path}'") +if git: +run_cmd(f"git rm -r -q '{ref_results_path}'") shutil.rmtree(ref_results_path) # Replace reference results with a freshly computed once. @@ -60,22 +62,11 @@ def run_cmd(command: str): # Clean up the generated diff erence results. SATestBuild.cleanup_reference_results(ref_results_path) -run_cmd(f"git add '{ref_results_path}'") +if git: +run_cmd(f"git add '{ref_results_path}'") -# TODO: use argparse -def main(argv): -if len(argv) == 2 and argv[1] in ("-h", "--help"): -print("Update static analyzer reference results based " - "\non the previous run of SATestBuild.py.\n" - "\nN.B.: Assumes that SATestBuild.py was just run", - file=sys.stderr) -sys.exit(1) - -project_map = ProjectMap() -for project in project_map.projects: -update_reference_results(project) - - -if __name__ == '__main__': -main(sys.argv) +if __name__ == "__main__": +print("SATestUpdateDiffs.py should not be used on its own.") +print("Please use 'SATest.py update' instead") +sys.exit(1) ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] b13d987 - [analyzer][solver] Track symbol equivalence
Author: Valeriy Savchenko Date: 2020-07-22T13:02:39+03:00 New Revision: b13d9878b8dcef4354ddfc86f382ca9b537e65aa URL: https://github.com/llvm/llvm-project/commit/b13d9878b8dcef4354ddfc86f382ca9b537e65aa DIFF: https://github.com/llvm/llvm-project/commit/b13d9878b8dcef4354ddfc86f382ca9b537e65aa.diff LOG: [analyzer][solver] Track symbol equivalence Summary: For the most cases, we try to reason about symbol either based on the information we know about that symbol in particular or about its composite parts. This is faster and eliminates costly brute force searches through existing constraints. However, we do want to support some cases that are widespread enough and involve reasoning about different existing constraints at once. These include: * resoning about 'a - b' based on what we know about 'b - a' * reasoning about 'a <= b' based on what we know about 'a > b' or 'a < b' This commit expands on that part by tracking symbols known to be equal while still avoiding brute force searches. It changes the way we track constraints for individual symbols. If we know for a fact that 'a == b' then there is no need in tracking constraints for both 'a' and 'b' especially if these constraints are different. This additional relationship makes dead/live logic for constraints harder as we want to maintain as much information on the equivalence class as possible, but we still won't carry the information that we don't need anymore. Differential Revision: https://reviews.llvm.org/D82445 Added: clang/test/Analysis/equality_tracking.c Modified: clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h index 365b1ff1bfe3..1aff2ca34a70 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h @@ -373,7 +373,7 @@ class SuppressInlineDefensiveChecksVisitor final : public BugReporterVisitor { class FalsePositiveRefutationBRVisitor final : public BugReporterVisitor { private: /// Holds the constraints in a given path - ConstraintRangeTy Constraints; + ConstraintMap Constraints; public: FalsePositiveRefutationBRVisitor(); @@ -390,7 +390,6 @@ class FalsePositiveRefutationBRVisitor final : public BugReporterVisitor { bool OverwriteConstraintsOnExistingSyms); }; - /// The visitor detects NoteTags and displays the event notes they contain. class TagVisitor : public BugReporterVisitor { public: diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h index 2f71f6fa..bc5d5f57cd68 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h @@ -136,14 +136,8 @@ class RangeSet { } }; -class ConstraintRange {}; -using ConstraintRangeTy = llvm::ImmutableMap; - -template <> -struct ProgramStateTrait -: public ProgramStatePartialTrait { - static void *GDMIndex(); -}; +using ConstraintMap = llvm::ImmutableMap; +ConstraintMap getConstraintMap(ProgramStateRef State); class RangedConstraintManager : public SimpleConstraintManager { public: @@ -222,4 +216,6 @@ class RangedConstraintManager : public SimpleConstraintManager { } // namespace ento } // namespace clang +REGISTER_FACTORY_WITH_PROGRAMSTATE(ConstraintMap) + #endif diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index ef4d38ff498f..bc72f4f8c1e3 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -2813,7 +2813,7 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, BugReporterContext , //===--===// FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor() -: Constraints(ConstraintRangeTy::Factory().getEmptyMap()) {} +: Constraints(ConstraintMap::Factory().getEmptyMap()) {} void FalsePositiveRefutationBRVisitor::finalizeVisitor( BugReporterContext , const ExplodedNode *EndPathNode, @@ -2855,9 +2855,8 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor( void FalsePositiveRefutationBRVisitor::addConstraints( const ExplodedNode *N, bool
[clang] f531c1c - [analyzer] Introduce small improvements to the solver infra
Author: Valeriy Savchenko Date: 2020-07-22T13:02:39+03:00 New Revision: f531c1c7c0d5850c824333518ff32708730d775b URL: https://github.com/llvm/llvm-project/commit/f531c1c7c0d5850c824333518ff32708730d775b DIFF: https://github.com/llvm/llvm-project/commit/f531c1c7c0d5850c824333518ff32708730d775b.diff LOG: [analyzer] Introduce small improvements to the solver infra Summary: * Add a new function to delete points from range sets. * Introduce an internal generic interface for range set intersections. * Remove unnecessary bits from a couple of solver functions. * Add in-code sections. Differential Revision: https://reviews.llvm.org/D82381 Added: Modified: clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h index a42eebd7d4e8..2f71f6fa 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h @@ -126,6 +126,8 @@ class RangeSet { RangeSet Intersect(BasicValueFactory , Factory , const RangeSet ) const; RangeSet Negate(BasicValueFactory , Factory ) const; + RangeSet Delete(BasicValueFactory , Factory , + const llvm::APSInt ) const; void print(raw_ostream ) const; @@ -139,11 +141,10 @@ using ConstraintRangeTy = llvm::ImmutableMap; template <> struct ProgramStateTrait - : public ProgramStatePartialTrait { +: public ProgramStatePartialTrait { static void *GDMIndex(); }; - class RangedConstraintManager : public SimpleConstraintManager { public: RangedConstraintManager(ExprEngine *EE, SValBuilder ) @@ -169,8 +170,8 @@ class RangedConstraintManager : public SimpleConstraintManager { protected: /// Assume a constraint between a symbolic expression and a concrete integer. virtual ProgramStateRef assumeSymRel(ProgramStateRef State, SymbolRef Sym, - BinaryOperator::Opcode op, - const llvm::APSInt ); + BinaryOperator::Opcode op, + const llvm::APSInt ); //===--===// // Interface that subclasses must implement. @@ -218,8 +219,7 @@ class RangedConstraintManager : public SimpleConstraintManager { static void computeAdjustment(SymbolRef , llvm::APSInt ); }; -} // end GR namespace - -} // end clang namespace +} // namespace ento +} // namespace clang #endif diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index cb6f61e86ae3..a5bb36e2026e 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -89,7 +89,7 @@ class OperatorRelationsTable { } TriStateKind getCmpOpState(BinaryOperatorKind CurrentOP, - BinaryOperatorKind QueriedOP) const { + BinaryOperatorKind QueriedOP) const { return CmpOpTable[getIndexFromOp(CurrentOP)][getIndexFromOp(QueriedOP)]; } @@ -364,6 +364,18 @@ RangeSet RangeSet::Negate(BasicValueFactory , Factory ) const { return newRanges; } +RangeSet RangeSet::Delete(BasicValueFactory , Factory , + const llvm::APSInt ) const { + llvm::APSInt Upper = Point; + llvm::APSInt Lower = Point; + + ++Upper; + --Lower; + + // Notice that the lower bound is greater than the upper bound. + return Intersect(BV, F, Upper, Lower); +} + void RangeSet::print(raw_ostream ) const { bool isFirst = true; os << "{ "; @@ -381,6 +393,107 @@ void RangeSet::print(raw_ostream ) const { namespace { +//===--===// +//Intersection functions +//===--===// + +template +LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory , + RangeSet::Factory , RangeSet Head, + SecondTy Second, RestTy... Tail); + +template struct IntersectionTraits; + +template struct IntersectionTraits { + // Found RangeSet, no need to check any further + using Type = RangeSet; +}; + +template <> struct IntersectionTraits<> { + // We ran out of types, and we didn't find any RangeSet, so the result should + // be optional. + using Type = Optional; +}; + +template +struct IntersectionTraits { + // If current type is Optional or a raw pointer, we should keep
[clang] e63b488 - [analyzer][solver] Track symbol disequalities
Author: Valeriy Savchenko Date: 2020-07-22T13:02:39+03:00 New Revision: e63b488f2755f91e8147fd678ed525cf6ba007cc URL: https://github.com/llvm/llvm-project/commit/e63b488f2755f91e8147fd678ed525cf6ba007cc DIFF: https://github.com/llvm/llvm-project/commit/e63b488f2755f91e8147fd678ed525cf6ba007cc.diff LOG: [analyzer][solver] Track symbol disequalities Summary: This commmit adds another relation that we can track separately from range constraints. Symbol disequality can help us understand that two equivalence classes are not equal to each other. We can generalize this knowledge to classes because for every a,b,c, and d that a == b, c == d, and b != c it is true that a != d. As a result, we can reason about other equalities/disequalities of symbols that we know nothing else about, i.e. no constraint ranges associated with them. However, we also benefit from the knowledge of disequal symbols by following the rule: if a != b and b == C where C is a constant, a != C This information can refine associated ranges for different classes and reduce the number of false positives and paths to explore. Differential Revision: https://reviews.llvm.org/D83286 Added: clang/test/Analysis/mutually_exclusive_null_fp.cpp Modified: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp clang/test/Analysis/equality_tracking.c Removed: diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 4c134b63efb0..32766d796add 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -401,6 +401,9 @@ REGISTER_MAP_WITH_PROGRAMSTATE(ClassMap, SymbolRef, EquivalenceClass) REGISTER_MAP_WITH_PROGRAMSTATE(ClassMembers, EquivalenceClass, SymbolSet) REGISTER_MAP_WITH_PROGRAMSTATE(ConstraintRange, EquivalenceClass, RangeSet) +REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(ClassSet, EquivalenceClass) +REGISTER_MAP_WITH_PROGRAMSTATE(DisequalityMap, EquivalenceClass, ClassSet) + namespace { /// This class encapsulates a set of symbols equal to each other. /// @@ -450,6 +453,26 @@ class EquivalenceClass : public llvm::FoldingSetNode { LLVM_NODISCARD inline bool isTriviallyDead(ProgramStateRef State, SymbolReaper ); + LLVM_NODISCARD static inline ProgramStateRef + markDisequal(BasicValueFactory , RangeSet::Factory , + ProgramStateRef State, SymbolRef First, SymbolRef Second); + LLVM_NODISCARD static inline ProgramStateRef + markDisequal(BasicValueFactory , RangeSet::Factory , + ProgramStateRef State, EquivalenceClass First, + EquivalenceClass Second); + LLVM_NODISCARD inline ProgramStateRef + markDisequal(BasicValueFactory , RangeSet::Factory , + ProgramStateRef State, EquivalenceClass Other) const; + LLVM_NODISCARD static inline ClassSet + getDisequalClasses(ProgramStateRef State, SymbolRef Sym); + LLVM_NODISCARD inline ClassSet + getDisequalClasses(ProgramStateRef State) const; + LLVM_NODISCARD inline ClassSet + getDisequalClasses(DisequalityMapTy Map, ClassSet::Factory ) const; + + LLVM_NODISCARD static inline Optional + areEqual(ProgramStateRef State, SymbolRef First, SymbolRef Second); + /// Check equivalence data for consistency. LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED static bool isClassDataConsistent(ProgramStateRef State); @@ -496,6 +519,11 @@ class EquivalenceClass : public llvm::FoldingSetNode { ProgramStateRef State, SymbolSet Members, EquivalenceClass Other, SymbolSet OtherMembers); + static inline void + addToDisequalityInfo(DisequalityMapTy , ConstraintRangeTy , + BasicValueFactory , RangeSet::Factory , + ProgramStateRef State, EquivalenceClass First, + EquivalenceClass Second); /// This is a unique identifier of the class. uintptr_t ID; @@ -505,17 +533,6 @@ class EquivalenceClass : public llvm::FoldingSetNode { // Constraint functions //===--===// -LLVM_NODISCARD inline ProgramStateRef setConstraint(ProgramStateRef State, -EquivalenceClass Class, -RangeSet Constraint) { - return State->set(Class, Constraint); -} - -LLVM_NODISCARD inline ProgramStateRef -setConstraint(ProgramStateRef State, SymbolRef Sym, RangeSet Constraint) { - return setConstraint(State, EquivalenceClass::find(State, Sym), Constraint); -} - LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State, EquivalenceClass
[clang] 5b4f143 - [analyzer][tests] Introduce analyzer benchmarking framework
Author: Valeriy Savchenko Date: 2020-07-14T11:42:46+03:00 New Revision: 5b4f143564502664a9d1197d6909047eab49530e URL: https://github.com/llvm/llvm-project/commit/5b4f143564502664a9d1197d6909047eab49530e DIFF: https://github.com/llvm/llvm-project/commit/5b4f143564502664a9d1197d6909047eab49530e.diff LOG: [analyzer][tests] Introduce analyzer benchmarking framework Summary: This commit includes a couple of changes: * Benchmark selected projects by analyzing them multiple times * Compare two benchmarking results and visualizing them on one chart * Organize project build logging, so we can use the same code in benchmarks Differential Revision: https://reviews.llvm.org/D83539 Added: clang/utils/analyzer/SATestBenchmark.py Modified: clang/utils/analyzer/SATest.py clang/utils/analyzer/SATestBuild.py clang/utils/analyzer/SATestUpdateDiffs.py clang/utils/analyzer/requirements.txt Removed: diff --git a/clang/utils/analyzer/SATest.py b/clang/utils/analyzer/SATest.py index 16f1dce0c584..46e636ad2895 100755 --- a/clang/utils/analyzer/SATest.py +++ b/clang/utils/analyzer/SATest.py @@ -34,29 +34,10 @@ def add(parser, args): def build(parser, args): import SATestBuild -from ProjectMap import ProjectMap SATestBuild.VERBOSE = args.verbose -project_map = ProjectMap() -projects = project_map.projects - -if args.projects: -projects_arg = args.projects.split(",") -available_projects = [project.name - for project in projects] - -# validate that given projects are present in the project map file -for manual_project in projects_arg: -if manual_project not in available_projects: -parser.error("Project '{project}' is not found in " - "the project map file. Available projects are " - "{all}.".format(project=manual_project, - all=available_projects)) - -projects = [project.with_fields(enabled=project.name in projects_arg) -for project in projects] - +projects = get_projects(parser, args.projects) tester = SATestBuild.RegressionTester(args.jobs, projects, args.override_compiler, @@ -100,6 +81,44 @@ def update(parser, args): SATestUpdateDiffs.update_reference_results(project) +def benchmark(parser, args): +from SATestBenchmark import Benchmark + +projects = get_projects(parser, args.projects) +benchmark = Benchmark(projects, args.iterations, args.output) +benchmark.run() + + +def benchmark_compare(parser, args): +import SATestBenchmark +SATestBenchmark.compare(args.old, args.new, args.output) + + +def get_projects(parser, projects_str): +from ProjectMap import ProjectMap + +project_map = ProjectMap() +projects = project_map.projects + +if projects_str: +projects_arg = projects_str.split(",") +available_projects = [project.name + for project in projects] + +# validate that given projects are present in the project map file +for manual_project in projects_arg: +if manual_project not in available_projects: +parser.error("Project '{project}' is not found in " + "the project map file. Available projects are " + "{all}.".format(project=manual_project, + all=available_projects)) + +projects = [project.with_fields(enabled=project.name in projects_arg) +for project in projects] + +return projects + + def docker(parser, args): if len(args.rest) > 0: if args.rest[0] != "--": @@ -284,6 +303,36 @@ def main(): "to the docker's entrypoint.") dock_parser.set_defaults(func=docker) +# benchmark subcommand +bench_parser = subparsers.add_parser( +"benchmark", +help="Run benchmarks by building a set of projects multiple times.") + +bench_parser.add_argument("-i", "--iterations", action="store", + type=int, default=20, + help="Number of iterations for building each " + "project.") +bench_parser.add_argument("-o", "--output", action="store", + default="benchmark.csv", + help="Output csv file for the benchmark results") +bench_parser.add_argument("--projects", action="store", default="", + help="Comma-separated list of projects to test") +bench_parser.set_defaults(func=benchmark) + +bench_subparsers = bench_parser.add_subparsers() +bench_compare_parser =
[clang] 089a0ad - [analyzer][tests] Add 5 more projects for testing
Author: Valeriy Savchenko Date: 2020-07-14T11:42:46+03:00 New Revision: 089a0ad8bc993923817d7957f08bd67dbecd56af URL: https://github.com/llvm/llvm-project/commit/089a0ad8bc993923817d7957f08bd67dbecd56af DIFF: https://github.com/llvm/llvm-project/commit/089a0ad8bc993923817d7957f08bd67dbecd56af.diff LOG: [analyzer][tests] Add 5 more projects for testing Differential Revision: https://reviews.llvm.org/D83701 Added: clang/utils/analyzer/projects/capnproto/cleanup_run_static_analyzer.sh clang/utils/analyzer/projects/capnproto/run_static_analyzer.cmd clang/utils/analyzer/projects/cppcheck/cleanup_run_static_analyzer.sh clang/utils/analyzer/projects/cppcheck/run_static_analyzer.cmd clang/utils/analyzer/projects/faiss/cleanup_run_static_analyzer.sh clang/utils/analyzer/projects/faiss/run_static_analyzer.cmd clang/utils/analyzer/projects/harfbuzz/cleanup_run_static_analyzer.sh clang/utils/analyzer/projects/harfbuzz/run_static_analyzer.cmd clang/utils/analyzer/projects/tmux/cleanup_run_static_analyzer.sh clang/utils/analyzer/projects/tmux/run_static_analyzer.cmd Modified: clang/utils/analyzer/Dockerfile clang/utils/analyzer/entrypoint.py clang/utils/analyzer/projects/projects.json Removed: diff --git a/clang/utils/analyzer/Dockerfile b/clang/utils/analyzer/Dockerfile index 21906011c7dc..f74ff8aa95c2 100644 --- a/clang/utils/analyzer/Dockerfile +++ b/clang/utils/analyzer/Dockerfile @@ -42,6 +42,16 @@ RUN apt-get install -y \ libjsonrpccpp-dev=0.7.0-1build2 \ uuid-dev=2.31.1-0.4ubuntu3.6 +# tmux dependencies +RUN apt-get install -y \ +autotools-dev=20180224.1 \ +automake=1:1.15.1-3ubuntu2 \ +libncurses5-dev=6.1-1ubuntu1.18.04 \ +libevent-dev=2.1.8-stable-4build1 \ +pkg-config=0.29.1-0ubuntu2 \ +flex=2.6.4-6 \ +bison=2:3.0.4.dfsg-1build1 + RUN update-alternatives --install /usr/bin/python python /usr/bin/python3 1 VOLUME /analyzer diff --git a/clang/utils/analyzer/entrypoint.py b/clang/utils/analyzer/entrypoint.py index b440e776b57c..9c84431da548 100644 --- a/clang/utils/analyzer/entrypoint.py +++ b/clang/utils/analyzer/entrypoint.py @@ -50,7 +50,7 @@ def is_cmake_needed(): CMAKE_COMMAND = "cmake -G Ninja -DCMAKE_BUILD_TYPE=Release " \ "-DCMAKE_INSTALL_PREFIX=/analyzer -DLLVM_TARGETS_TO_BUILD=X86 " \ -"-DLLVM_ENABLE_PROJECTS=clang -DLLVM_BUILD_RUNTIME=OFF " \ +"-DLLVM_ENABLE_PROJECTS=\"clang;openmp\" -DLLVM_BUILD_RUNTIME=OFF " \ "-DLLVM_ENABLE_TERMINFO=OFF -DCLANG_ENABLE_ARCMT=OFF " \ "-DCLANG_ENABLE_STATIC_ANALYZER=ON" diff --git a/clang/utils/analyzer/projects/capnproto/cleanup_run_static_analyzer.sh b/clang/utils/analyzer/projects/capnproto/cleanup_run_static_analyzer.sh new file mode 100755 index ..e14c423280ec --- /dev/null +++ b/clang/utils/analyzer/projects/capnproto/cleanup_run_static_analyzer.sh @@ -0,0 +1 @@ +rm -rf ./build diff --git a/clang/utils/analyzer/projects/capnproto/run_static_analyzer.cmd b/clang/utils/analyzer/projects/capnproto/run_static_analyzer.cmd new file mode 100644 index ..6678fe635db3 --- /dev/null +++ b/clang/utils/analyzer/projects/capnproto/run_static_analyzer.cmd @@ -0,0 +1,2 @@ +cmake . -DCMAKE_BUILD_TYPE=Debug -Bbuild -GNinja +cmake --build build diff --git a/clang/utils/analyzer/projects/cppcheck/cleanup_run_static_analyzer.sh b/clang/utils/analyzer/projects/cppcheck/cleanup_run_static_analyzer.sh new file mode 100755 index ..e14c423280ec --- /dev/null +++ b/clang/utils/analyzer/projects/cppcheck/cleanup_run_static_analyzer.sh @@ -0,0 +1 @@ +rm -rf ./build diff --git a/clang/utils/analyzer/projects/cppcheck/run_static_analyzer.cmd b/clang/utils/analyzer/projects/cppcheck/run_static_analyzer.cmd new file mode 100644 index ..72cb7f7677e6 --- /dev/null +++ b/clang/utils/analyzer/projects/cppcheck/run_static_analyzer.cmd @@ -0,0 +1,2 @@ +cmake . -DCMAKE_BUILD_TYPE=Debug -DCMAKE_DISABLE_PRECOMPILE_HEADERS=ON -Bbuild -GNinja +cmake --build build diff --git a/clang/utils/analyzer/projects/faiss/cleanup_run_static_analyzer.sh b/clang/utils/analyzer/projects/faiss/cleanup_run_static_analyzer.sh new file mode 100755 index ..efcd16e590a1 --- /dev/null +++ b/clang/utils/analyzer/projects/faiss/cleanup_run_static_analyzer.sh @@ -0,0 +1 @@ +make clean diff --git a/clang/utils/analyzer/projects/faiss/run_static_analyzer.cmd b/clang/utils/analyzer/projects/faiss/run_static_analyzer.cmd new file mode 100644 index ..877fa2aa389b --- /dev/null +++ b/clang/utils/analyzer/projects/faiss/run_static_analyzer.cmd @@ -0,0 +1,2 @@ +./configure --without-cuda +make diff --git a/clang/utils/analyzer/projects/harfbuzz/cleanup_run_static_analyzer.sh b/clang/utils/analyzer/projects/harfbuzz/cleanup_run_static_analyzer.sh new file mode 100755 index ..e14c423280ec --- /dev/null +++
[clang] e124062 - Fix bad doxygen result for class clang::ento::CallEvent and its derived classes
Author: Ella Ma Date: 2020-07-13T12:25:26+03:00 New Revision: e124062bf3874e1ce7ddad407b35e95ec3d3ac13 URL: https://github.com/llvm/llvm-project/commit/e124062bf3874e1ce7ddad407b35e95ec3d3ac13 DIFF: https://github.com/llvm/llvm-project/commit/e124062bf3874e1ce7ddad407b35e95ec3d3ac13.diff LOG: Fix bad doxygen result for class clang::ento::CallEvent and its derived classes Summary: Fix bug https://bugs.llvm.org/show_bug.cgi?id=44753. This patch is a workaround of a Doxygen bug, so that it can correctly generate documents for class clang::ento::CallEvent and its derived classes. Differential Revision: https://reviews.llvm.org/D82356 Added: Modified: clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index d75f9f63286d..a2a98c558a4b 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -554,7 +554,7 @@ class SimpleFunctionCall : public AnyFunctionCall { /// Represents a call to a block. /// -/// Example: ^{ /* ... */ }() +/// Example: ^{ statement-body }() class BlockCall : public CallEvent { friend class CallEventManager; ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 9c7ff0a - [analyzer][tests] Make test interruption safe
Author: Valeriy Savchenko Date: 2020-07-10T11:31:59+03:00 New Revision: 9c7ff0a4aaef0a1efa33e9ff8a12a064087bded9 URL: https://github.com/llvm/llvm-project/commit/9c7ff0a4aaef0a1efa33e9ff8a12a064087bded9 DIFF: https://github.com/llvm/llvm-project/commit/9c7ff0a4aaef0a1efa33e9ff8a12a064087bded9.diff LOG: [analyzer][tests] Make test interruption safe Differential Revision: https://reviews.llvm.org/D83373 Added: Modified: clang/utils/analyzer/SATest.py Removed: diff --git a/clang/utils/analyzer/SATest.py b/clang/utils/analyzer/SATest.py index fb2e031c6ab6..16f1dce0c584 100755 --- a/clang/utils/analyzer/SATest.py +++ b/clang/utils/analyzer/SATest.py @@ -132,27 +132,35 @@ def docker_shell(args): pass finally: -print("Please wait for docker to clean up") -call("docker stop satest", shell=True) +docker_cleanup() def docker_run(args, command, docker_args=""): -return call("docker run --rm --name satest " -"-v {llvm}:/llvm-project " -"-v {build}:/build " -"-v {clang}:/analyzer " -"-v {scripts}:/scripts " -"-v {projects}:/projects " -"{docker_args} " -"satest-image:latest {command}" -.format(llvm=args.llvm_project_dir, -build=args.build_dir, -clang=args.clang_dir, -scripts=SCRIPTS_DIR, -projects=PROJECTS_DIR, -docker_args=docker_args, -command=command), -shell=True) +try: +return call("docker run --rm --name satest " +"-v {llvm}:/llvm-project " +"-v {build}:/build " +"-v {clang}:/analyzer " +"-v {scripts}:/scripts " +"-v {projects}:/projects " +"{docker_args} " +"satest-image:latest {command}" +.format(llvm=args.llvm_project_dir, +build=args.build_dir, +clang=args.clang_dir, +scripts=SCRIPTS_DIR, +projects=PROJECTS_DIR, +docker_args=docker_args, +command=command), +shell=True) + +except KeyboardInterrupt: +docker_cleanup() + + +def docker_cleanup(): +print("Please wait for docker to clean up") +call("docker stop satest", shell=True) def main(): ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 00997d1 - [analyzer][tests] Fix zip unpacking
Author: Valeriy Savchenko Date: 2020-07-10T11:32:13+03:00 New Revision: 00997d1cad9ecd40c92aeae40269f6bfb9e58d11 URL: https://github.com/llvm/llvm-project/commit/00997d1cad9ecd40c92aeae40269f6bfb9e58d11 DIFF: https://github.com/llvm/llvm-project/commit/00997d1cad9ecd40c92aeae40269f6bfb9e58d11.diff LOG: [analyzer][tests] Fix zip unpacking Differential Revision: https://reviews.llvm.org/D83374 Added: Modified: clang/utils/analyzer/SATestBuild.py Removed: diff --git a/clang/utils/analyzer/SATestBuild.py b/clang/utils/analyzer/SATestBuild.py index ee510e03cc5a..eefab869f6ef 100644 --- a/clang/utils/analyzer/SATestBuild.py +++ b/clang/utils/analyzer/SATestBuild.py @@ -601,7 +601,7 @@ def _download_from_git(self, directory: str, build_log_file: IO): stdout=build_log_file, shell=True) def _unpack_zip(self, directory: str, build_log_file: IO): -zip_files = list(glob.glob(os.path.join(directory, "/*.zip"))) +zip_files = list(glob.glob(directory + "/*.zip")) if len(zip_files) == 0: raise ValueError( ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 21bacc2 - [analyzer][tests] Measure peak memory consumption for every project
Author: Valeriy Savchenko Date: 2020-07-10T11:31:41+03:00 New Revision: 21bacc215413d10df53a4690e9561e9b96698742 URL: https://github.com/llvm/llvm-project/commit/21bacc215413d10df53a4690e9561e9b96698742 DIFF: https://github.com/llvm/llvm-project/commit/21bacc215413d10df53a4690e9561e9b96698742.diff LOG: [analyzer][tests] Measure peak memory consumption for every project Differential Revision: https://reviews.llvm.org/D82967 Added: clang/utils/analyzer/requirements.txt Modified: clang/utils/analyzer/Dockerfile clang/utils/analyzer/SATestBuild.py clang/utils/analyzer/SATestUtils.py Removed: diff --git a/clang/utils/analyzer/Dockerfile b/clang/utils/analyzer/Dockerfile index 30fb67cf93c8..21906011c7dc 100644 --- a/clang/utils/analyzer/Dockerfile +++ b/clang/utils/analyzer/Dockerfile @@ -54,8 +54,7 @@ ENV PATH="/analyzer/bin:${PATH}" ADD entrypoint.py /entrypoint.py -# Uncomment in case of requirements -# ADD requirements.txt /requirements.txt -# RUN pip3 install -r /requirements.txt +ADD requirements.txt /requirements.txt +RUN pip3 install -r /requirements.txt ENTRYPOINT ["python", "/entrypoint.py"] diff --git a/clang/utils/analyzer/SATestBuild.py b/clang/utils/analyzer/SATestBuild.py index 7d337632744f..ee510e03cc5a 100644 --- a/clang/utils/analyzer/SATestBuild.py +++ b/clang/utils/analyzer/SATestBuild.py @@ -43,7 +43,7 @@ variable. It should contain a comma separated list. """ import CmpRuns -import SATestUtils +import SATestUtils as utils from ProjectMap import DownloadType, ProjectInfo import glob @@ -63,7 +63,7 @@ # and this is we can shush that false positive from plistlib import InvalidFileException # type:ignore from subprocess import CalledProcessError, check_call -from typing import Dict, IO, List, NamedTuple, Optional, TYPE_CHECKING +from typing import Dict, IO, List, NamedTuple, Optional, TYPE_CHECKING, Tuple ### @@ -115,7 +115,7 @@ def stdout(message: str): if 'CC' in os.environ: cc_candidate: Optional[str] = os.environ['CC'] else: -cc_candidate = SATestUtils.which("clang", os.environ['PATH']) +cc_candidate = utils.which("clang", os.environ['PATH']) if not cc_candidate: stderr("Error: cannot find 'clang' in PATH") sys.exit(1) @@ -194,9 +194,9 @@ def run_cleanup_script(directory: str, build_log_file: IO): cwd = os.path.join(directory, PATCHED_SOURCE_DIR_NAME) script_path = os.path.join(directory, CLEANUP_SCRIPT) -SATestUtils.run_script(script_path, build_log_file, cwd, - out=LOCAL.stdout, err=LOCAL.stderr, - verbose=VERBOSE) +utils.run_script(script_path, build_log_file, cwd, + out=LOCAL.stdout, err=LOCAL.stderr, + verbose=VERBOSE) class TestInfo(NamedTuple): @@ -351,8 +351,6 @@ def get_output_dir(self) -> str: return OUTPUT_DIR_NAME def build(self, directory: str, output_dir: str): -time_start = time.time() - build_log_path = get_build_log_path(output_dir) stdout(f"Log file: {build_log_path}\n") @@ -375,19 +373,23 @@ def build(self, directory: str, output_dir: str): if self.project.mode == 1: self._download_and_patch(directory, build_log_file) run_cleanup_script(directory, build_log_file) -self.scan_build(directory, output_dir, build_log_file) +build_time, memory = self.scan_build(directory, output_dir, + build_log_file) else: -self.analyze_preprocessed(directory, output_dir) +build_time, memory = self.analyze_preprocessed(directory, + output_dir) if self.is_reference_build: run_cleanup_script(directory, build_log_file) normalize_reference_results(directory, output_dir, self.project.mode) -stdout(f"Build complete (time: {time.time() - time_start:.2f}). " +stdout(f"Build complete (time: {utils.time_to_str(build_time)}, " + f"peak memory: {utils.memory_to_str(memory)}). " f"See the log for more details: {build_log_path}\n") -def scan_build(self, directory: str, output_dir: str, build_log_file: IO): +def scan_build(self, directory: str, output_dir: str, + build_log_file: IO) -> Tuple[float, int]: """ Build the project with scan-build by reading in the commands and prefixing them with the scan-build options. @@ -416,6 +418,10 @@ def scan_build(self, directory: str, output_dir: str, build_log_file: IO): options += "--override-compiler " extra_env:
[clang] 3770f5c - [analyzer] SATest: Add convenience 'docker' command
Author: Valeriy Savchenko Date: 2020-06-25T12:28:22+03:00 New Revision: 3770f5c9b98c5bae2f099f5c24e05eb4a0cca1d0 URL: https://github.com/llvm/llvm-project/commit/3770f5c9b98c5bae2f099f5c24e05eb4a0cca1d0 DIFF: https://github.com/llvm/llvm-project/commit/3770f5c9b98c5bae2f099f5c24e05eb4a0cca1d0.diff LOG: [analyzer] SATest: Add convenience 'docker' command Summary: It provides a simpler interface for testing within docker. This way the user is not required to no how to use `docker run` and its options. Differential Revision: https://reviews.llvm.org/D81572 Added: Modified: clang/utils/analyzer/SATest.py Removed: diff --git a/clang/utils/analyzer/SATest.py b/clang/utils/analyzer/SATest.py index f45f593d08ec..f7cf5146566d 100755 --- a/clang/utils/analyzer/SATest.py +++ b/clang/utils/analyzer/SATest.py @@ -9,6 +9,16 @@ import argparse import sys +import os + +from subprocess import check_call + +SCRIPTS_DIR = os.path.dirname(os.path.realpath(__file__)) +PROJECTS_DIR = os.path.join(SCRIPTS_DIR, "projects") +DEFAULT_LLVM_DIR = os.path.realpath(os.path.join(SCRIPTS_DIR, + os.path.pardir, + os.path.pardir, + os.path.pardir)) def add(parser, args): @@ -78,6 +88,37 @@ def update(parser, args): SATestUpdateDiffs.update_reference_results(project) +def docker(parser, args): +if len(args.rest) > 0: +if args.rest[0] != "--": +parser.error("REST arguments should start with '--'") +args.rest = args.rest[1:] + +if args.build_image: +docker_build_image() +else: +docker_run(args) + + +def docker_build_image(): +check_call("docker build --tag satest-image {}".format(SCRIPTS_DIR), + shell=True) + + +def docker_run(args): +check_call("docker run --rm --name satest " + "-v {llvm}:/llvm-project " + "-v {build}:/build " + "-v {clang}:/analyzer " + "-v {scripts}:/scripts " + "-v {projects}:/projects " + "satest-image:latest {args}" + .format(llvm=args.llvm_project_dir, build=args.build_dir, + clang=args.clang_dir, scripts=SCRIPTS_DIR, + projects=PROJECTS_DIR, args=' '.join(args.rest)), + shell=True) + + def main(): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers() @@ -180,6 +221,27 @@ def main(): # TODO: add option to decide whether we should use git upd_parser.set_defaults(func=update) +# docker subcommand +dock_parser = subparsers.add_parser( +"docker", +help="Run regression system in the docker.") + +dock_parser.add_argument("--build-image", action="store_true", + help="Build docker image for running tests.") +dock_parser.add_argument("--llvm-project-dir", action="store", + default=DEFAULT_LLVM_DIR, + help="Path to LLVM source code. Defaults " + "to the repo where this script is located. ") +dock_parser.add_argument("--build-dir", action="store", default="", + help="Path to a directory where docker should " + "build LLVM code.") +dock_parser.add_argument("--clang-dir", action="store", default="", + help="Path to find/install LLVM installation.") +dock_parser.add_argument("rest", nargs=argparse.REMAINDER, default=[], + help="Additionall args that will be forwarded " + "to the docker's entrypoint.") +dock_parser.set_defaults(func=docker) + args = parser.parse_args() args.func(parser, args) ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] e010d14 - [analyzer] SATest: Add initial docker infrastructure
Author: Valeriy Savchenko Date: 2020-06-25T12:28:21+03:00 New Revision: e010d1432fa15cff7a6d369f5e372d995e2eba5d URL: https://github.com/llvm/llvm-project/commit/e010d1432fa15cff7a6d369f5e372d995e2eba5d DIFF: https://github.com/llvm/llvm-project/commit/e010d1432fa15cff7a6d369f5e372d995e2eba5d.diff LOG: [analyzer] SATest: Add initial docker infrastructure Summary: Static analysis is very sensitive to environment. OS and libraries installed can affect the results. This fact makes it extremely hard to have a regression testing system that will produce stable results. For this very reason, this commit introduces a new dockerized testing environment, so that every analyzer developer can check their changes against previous analysis results. Differential Revision: https://reviews.llvm.org/D81571 Added: clang/utils/analyzer/Dockerfile clang/utils/analyzer/entrypoint.py Modified: Removed: diff --git a/clang/utils/analyzer/Dockerfile b/clang/utils/analyzer/Dockerfile new file mode 100644 index ..e87377c9eec1 --- /dev/null +++ b/clang/utils/analyzer/Dockerfile @@ -0,0 +1,52 @@ +FROM ubuntu:bionic + +RUN apt-get update && apt-get install -y \ +apt-transport-https \ +ca-certificates \ +gnupg \ +software-properties-common \ +wget + +# newer CMake is required by LLVM +RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null +RUN apt-add-repository -y 'deb https://apt.kitware.com/ubuntu/ bionic main' + +# test system dependencies +RUN apt-get update && apt-get install -y \ +git \ +gettext \ +python3 \ +python3-pip \ +cmake \ +ninja-build + +# box2d dependencies +RUN apt-get install -y \ +libx11-dev \ +libxrandr-dev \ +libxinerama-dev \ +libxcursor-dev \ +libxi-dev + +# symengine dependencies +RUN apt-get install -y \ +libgmp10 \ +libgmp-dev + +RUN update-alternatives --install /usr/bin/python python /usr/bin/python3 1 + +VOLUME /analyzer +VOLUME /projects +VOLUME /llvm-project +VOLUME /build +VOLUME /scripts + +ENV PATH="/analyzer/bin:${PATH}" + +ADD entrypoint.py /entrypoint.py + +# Uncomment in case of requirements +# ADD requirements.txt /requirements.txt +# RUN pip3 install -r /requirements.txt + +ENTRYPOINT ["python", "/entrypoint.py"] diff --git a/clang/utils/analyzer/entrypoint.py b/clang/utils/analyzer/entrypoint.py new file mode 100644 index ..3b85628a9a85 --- /dev/null +++ b/clang/utils/analyzer/entrypoint.py @@ -0,0 +1,52 @@ +import argparse +import os + +from typing import List, Tuple + +from subprocess import check_call + + +def main(): +settings, rest = parse_arguments() +if settings.build_llvm or settings.build_llvm_only: +build_llvm() +if settings.build_llvm_only: +return +test(rest) + + +def parse_arguments() -> Tuple[argparse.Namespace, List[str]]: +parser = argparse.ArgumentParser() +parser.add_argument('--build-llvm', action='store_true') +parser.add_argument('--build-llvm-only', action='store_true') +return parser.parse_known_args() + + +def build_llvm() -> None: +os.chdir('/build') +cmake() +ninja() + + +CMAKE_COMMAND = "cmake -G Ninja -DCMAKE_BUILD_TYPE=Release " \ +"-DCMAKE_INSTALL_PREFIX=/analyzer -DLLVM_TARGETS_TO_BUILD=X86 " \ +"-DLLVM_ENABLE_PROJECTS=clang -DLLVM_BUILD_RUNTIME=OFF " \ +"-DLLVM_ENABLE_TERMINFO=OFF -DCLANG_ENABLE_ARCMT=OFF " \ +"-DCLANG_ENABLE_STATIC_ANALYZER=ON" + + +def cmake(): +check_call(CMAKE_COMMAND + ' /llvm-project/llvm', shell=True) + + +def ninja(): +check_call("ninja install", shell=True) + + +def test(args: List[str]): +os.chdir("/projects") +check_call("/scripts/SATest.py " + " ".join(args), shell=True) + + +if __name__ == '__main__': +main() ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 14b947f - [analyzer] Fix StdLibraryFunctionsChecker crash on macOS
Author: Valeriy Savchenko Date: 2020-06-16T16:10:07+03:00 New Revision: 14b947f306ac7bc2e4eb55ac1e4255fd762b217b URL: https://github.com/llvm/llvm-project/commit/14b947f306ac7bc2e4eb55ac1e4255fd762b217b DIFF: https://github.com/llvm/llvm-project/commit/14b947f306ac7bc2e4eb55ac1e4255fd762b217b.diff LOG: [analyzer] Fix StdLibraryFunctionsChecker crash on macOS Summary: EOF macro token coming from a PCH file on macOS while marked as literal, doesn't contain any literal data. This causes crash on every project using PCHs. This commit doesn't resolve the problem with PCH (maybe it was designed like this for a purpose) or with `tryExpandAsInteger`, but rather simply shoots off a crash itself. Differential Revision: https://reviews.llvm.org/D81916 Added: clang/test/Analysis/pch_crash.cpp Modified: clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp index 4b63ebc40ede..cae728815b41 100644 --- a/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp @@ -128,7 +128,9 @@ llvm::Optional tryExpandAsInteger(StringRef Macro, // Parse an integer at the end of the macro definition. const Token = FilteredTokens.back(); - if (!T.isLiteral()) + // FIXME: EOF macro token coming from a PCH file on macOS while marked as + //literal, doesn't contain any literal data + if (!T.isLiteral() || !T.getLiteralData()) return llvm::None; StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength()); llvm::APInt IntValue; diff --git a/clang/test/Analysis/pch_crash.cpp b/clang/test/Analysis/pch_crash.cpp new file mode 100644 index ..7ad2cb2d2ab5 --- /dev/null +++ b/clang/test/Analysis/pch_crash.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.15.0 -emit-pch -o %t %s +// RUN: %clang_analyze_cc1 -triple x86_64-apple-macosx10.15.0 -include-pch %t \ +// RUN: -analyzer-checker=core,apiModeling -verify %s +// +// RUN: %clang_cc1 -emit-pch -o %t %s +// RUN: %clang_analyze_cc1 -include-pch %t \ +// RUN: -analyzer-checker=core,apiModeling -verify %s + +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER +// Pre-compiled header + +int foo(); + +// Literal data for this macro value will be null +#define EOF -1 + +#else +// Source file + +int test() { + // we need a function call here to initiate erroneous routine + return foo(); // no-crash +} + +#endif ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 4a606e0 - [analyzer] CmpRuns.py: Fix error due to statistics differences
Author: Valeriy Savchenko Date: 2020-06-16T13:30:00+03:00 New Revision: 4a606e0a8c8f196832049c9a6c297589dacedb04 URL: https://github.com/llvm/llvm-project/commit/4a606e0a8c8f196832049c9a6c297589dacedb04 DIFF: https://github.com/llvm/llvm-project/commit/4a606e0a8c8f196832049c9a6c297589dacedb04.diff LOG: [analyzer] CmpRuns.py: Fix error due to statistics differences Differential Revision: https://reviews.llvm.org/D81642 Added: Modified: clang/utils/analyzer/CmpRuns.py Removed: diff --git a/clang/utils/analyzer/CmpRuns.py b/clang/utils/analyzer/CmpRuns.py index d94d0bfc83b5..5199a87a8205 100755 --- a/clang/utils/analyzer/CmpRuns.py +++ b/clang/utils/analyzer/CmpRuns.py @@ -398,16 +398,18 @@ def compare_stats(results_old: AnalysisRun, results_new: AnalysisRun): stats_old = derive_stats(results_old) stats_new = derive_stats(results_new) -keys = sorted(stats_old.keys()) +old_keys = set(stats_old.keys()) +new_keys = set(stats_new.keys()) +keys = sorted(old_keys & new_keys) -# FIXME: stats_old and stats_new are not necessarily sharing all of their -#stats and can crash when stats_new doesn't have/removed some for key in keys: print(key) -for kkey in stats_old[key]: -val_old = float(stats_old[key][kkey]) -val_new = float(stats_new[key][kkey]) +nested_keys = sorted(set(stats_old[key]) & set(stats_new[key])) + +for nested_key in nested_keys: +val_old = float(stats_old[key][nested_key]) +val_new = float(stats_new[key][nested_key]) report = f"{val_old:.3f} -> {val_new:.3f}" @@ -420,7 +422,17 @@ def compare_stats(results_old: AnalysisRun, results_new: AnalysisRun): elif ratio > 0.2: report = Colors.RED + report + Colors.CLEAR -print(f"\t {kkey} {report}") +print(f"\t {nested_key} {report}") + +removed_keys = old_keys - new_keys +if removed_keys: +print(f"REMOVED statistics: {removed_keys}") + +added_keys = new_keys - old_keys +if added_keys: +print(f"ADDED statistics: {added_keys}") + +print() def dump_scan_build_results_ diff (dir_old: str, dir_new: str, ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 38b455e - [analyzer] SATest: Add option to specify projects to test
Author: Valeriy Savchenko Date: 2020-06-16T13:30:01+03:00 New Revision: 38b455e91a63cc20c24b19ab4f469f7c6c60db44 URL: https://github.com/llvm/llvm-project/commit/38b455e91a63cc20c24b19ab4f469f7c6c60db44 DIFF: https://github.com/llvm/llvm-project/commit/38b455e91a63cc20c24b19ab4f469f7c6c60db44.diff LOG: [analyzer] SATest: Add option to specify projects to test Differential Revision: https://reviews.llvm.org/D81569 Added: Modified: clang/utils/analyzer/ProjectMap.py clang/utils/analyzer/SATest.py clang/utils/analyzer/SATestBuild.py Removed: diff --git a/clang/utils/analyzer/ProjectMap.py b/clang/utils/analyzer/ProjectMap.py index 62b7e48fde9b..3daa70140562 100644 --- a/clang/utils/analyzer/ProjectMap.py +++ b/clang/utils/analyzer/ProjectMap.py @@ -28,6 +28,20 @@ class ProjectInfo(NamedTuple): commit: str = "" enabled: bool = True +def with_fields(self, **kwargs) -> "ProjectInfo": +""" +Create a copy of this project info with customized fields. +NamedTuple is immutable and this is a way to create modified copies. + + info.enabled = True + info.mode = 1 + +can be done as follows: + + modified = info.with_fields(enbled=True, mode=1) +""" +return ProjectInfo(**{**self._asdict(), **kwargs}) + class ProjectMap: """ diff --git a/clang/utils/analyzer/SATest.py b/clang/utils/analyzer/SATest.py index 4fb7bf749947..f45f593d08ec 100755 --- a/clang/utils/analyzer/SATest.py +++ b/clang/utils/analyzer/SATest.py @@ -28,7 +28,29 @@ def add(parser, args): def build(parser, args): SATestBuild.VERBOSE = args.verbose -tester = SATestBuild.RegressionTester(args.jobs, args.override_compiler, + +project_map = ProjectMap() +projects = project_map.projects + +if args.projects: +projects_arg = args.projects.split(",") +available_projects = [project.name + for project in projects] + +# validate that given projects are present in the project map file +for manual_project in projects_arg: +if manual_project not in available_projects: +parser.error("Project '{project}' is not found in " + "the project map file. Available projects are " + "{all}.".format(project=manual_project, + all=available_projects)) + +projects = [project.with_fields(enabled=project.name in projects_arg) +for project in projects] + +tester = SATestBuild.RegressionTester(args.jobs, + projects, + args.override_compiler, args.extra_analyzer_config, args.regenerate, args.strictness) @@ -111,6 +133,8 @@ def main(): dest="extra_analyzer_config", type=str, default="", help="Arguments passed to to -analyzer-config") +build_parser.add_argument("--projects", action="store", default="", + help="Comma-separated list of projects to test") build_parser.add_argument("-v", "--verbose", action="count", default=0) build_parser.set_defaults(func=build) diff --git a/clang/utils/analyzer/SATestBuild.py b/clang/utils/analyzer/SATestBuild.py index d5b0710c8d61..d83ff1e7d009 100644 --- a/clang/utils/analyzer/SATestBuild.py +++ b/clang/utils/analyzer/SATestBuild.py @@ -44,7 +44,7 @@ """ import CmpRuns import SATestUtils -from ProjectMap import DownloadType, ProjectInfo, ProjectMap +from ProjectMap import DownloadType, ProjectInfo import glob import logging @@ -225,10 +225,11 @@ class RegressionTester: """ A component aggregating all of the project testing. """ -def __init__(self, jobs: int, override_compiler: bool, - extra_analyzer_config: str, regenerate: bool, - strictness: bool): +def __init__(self, jobs: int, projects: List[ProjectInfo], + override_compiler: bool, extra_analyzer_config: str, + regenerate: bool, strictness: bool): self.jobs = jobs +self.projects = projects self.override_compiler = override_compiler self.extra_analyzer_config = extra_analyzer_config self.regenerate = regenerate @@ -237,10 +238,8 @@ def __init__(self, jobs: int, override_compiler: bool, def test_all(self) -> bool: projects_to_test: List[TestInfo] = [] -project_map = ProjectMap() - # Test the projects. -for project in project_map.projects: +for project in self.projects: projects_to_test.append(
[clang] 98f737f - [analyzer] CmpRuns.py: Refactor and add type annotations. NFC.
Author: Valeriy Savchenko Date: 2020-06-16T13:29:46+03:00 New Revision: 98f737f4bfc79acac31d899886ad6b5d44396bde URL: https://github.com/llvm/llvm-project/commit/98f737f4bfc79acac31d899886ad6b5d44396bde DIFF: https://github.com/llvm/llvm-project/commit/98f737f4bfc79acac31d899886ad6b5d44396bde.diff LOG: [analyzer] CmpRuns.py: Refactor and add type annotations. NFC. Differential Revision: https://reviews.llvm.org/D80517 Added: Modified: clang/utils/analyzer/CmpRuns.py clang/utils/analyzer/SATestBuild.py Removed: diff --git a/clang/utils/analyzer/CmpRuns.py b/clang/utils/analyzer/CmpRuns.py index 28e9258f593a..d94d0bfc83b5 100755 --- a/clang/utils/analyzer/CmpRuns.py +++ b/clang/utils/analyzer/CmpRuns.py @@ -17,26 +17,35 @@ # Load the results of both runs, to obtain lists of the corresponding # AnalysisDiagnostic objects. # -resultsA = loadResultsFromSingleRun(singleRunInfoA, deleteEmpty) -resultsB = loadResultsFromSingleRun(singleRunInfoB, deleteEmpty) +resultsA = load_results_from_single_run(singleRunInfoA, delete_empty) +resultsB = load_results_from_single_run(singleRunInfoB, delete_empty) # Generate a relation from diagnostics in run A to diagnostics in run B # to obtain a list of triples (a, b, confidence). - diff = compareResults(resultsA, resultsB) + diff = compare_results(resultsA, resultsB) """ -from __future__ import division, print_function - -from collections import defaultdict - -from math import log -from optparse import OptionParser +import argparse import json import os import plistlib import re import sys +from math import log +from collections import defaultdict +from copy import copy +from typing import (Any, cast, Dict, List, Optional, Sequence, TextIO, TypeVar, +Tuple, Union) + + +Number = Union[int, float] +Stats = Dict[str, Dict[str, Number]] +Plist = Dict[str, Any] +JSON = Dict[str, Any] +# Type for generics +T = TypeVar('T') + STATS_REGEXP = re.compile(r"Statistics: (\{.+\})", re.MULTILINE | re.DOTALL) @@ -56,118 +65,127 @@ class SingleRunInfo: root - the name of the root directory, which will be disregarded when determining the source file name """ -def __init__(self, path, root="", verboseLog=None): +def __init__(self, path: str, root: str = "", verbose_log=None): self.path = path self.root = root.rstrip("/\\") -self.verboseLog = verboseLog +self.verbose_log = verbose_log class AnalysisDiagnostic: -def __init__(self, data, report, htmlReport): +def __init__(self, data: Plist, report: "AnalysisReport", + html_report: Optional[str]): self._data = data self._loc = self._data['location'] self._report = report -self._htmlReport = htmlReport -self._reportSize = len(self._data['path']) +self._html_report = html_report +self._report_size = len(self._data['path']) -def getFileName(self): +def get_file_name(self) -> str: root = self._report.run.root -fileName = self._report.files[self._loc['file']] -if fileName.startswith(root) and len(root) > 0: -return fileName[len(root) + 1:] -return fileName +file_name = self._report.files[self._loc['file']] + +if file_name.startswith(root) and len(root) > 0: +return file_name[len(root) + 1:] -def getRootFileName(self): +return file_name + +def get_root_file_name(self) -> str: path = self._data['path'] + if not path: -return self.getFileName() +return self.get_file_name() + p = path[0] if 'location' in p: -fIdx = p['location']['file'] +file_index = p['location']['file'] else: # control edge -fIdx = path[0]['edges'][0]['start'][0]['file'] -out = self._report.files[fIdx] +file_index = path[0]['edges'][0]['start'][0]['file'] + +out = self._report.files[file_index] root = self._report.run.root + if out.startswith(root): return out[len(root):] + return out -def getLine(self): +def get_line(self) -> int: return self._loc['line'] -def getColumn(self): +def get_column(self) -> int: return self._loc['col'] -def getPathLength(self): -return self._reportSize +def get_path_length(self) -> int: +return self._report_size -def getCategory(self): +def get_category(self) -> str: return self._data['category'] -def getDescription(self): +def get_description(self) -> str: return self._data['description'] -def getIssueIdentifier(self): -id = self.getFileName() + "+" -if 'issue_context' in self._data: -id +=
[clang] 35dd014 - [analyzer] CmpRuns.py: Decouple main functionality from argparse
Author: Valeriy Savchenko Date: 2020-06-16T13:30:01+03:00 New Revision: 35dd0147cdd0b8a145592d895d0a64eedb397917 URL: https://github.com/llvm/llvm-project/commit/35dd0147cdd0b8a145592d895d0a64eedb397917 DIFF: https://github.com/llvm/llvm-project/commit/35dd0147cdd0b8a145592d895d0a64eedb397917.diff LOG: [analyzer] CmpRuns.py: Decouple main functionality from argparse Summary: It makes it much harder to use from other modules when one of the parameters is an argparse Namespace. This commit makes it easier to use CmpRuns programmatically. Differential Revision: https://reviews.llvm.org/D81566 Added: Modified: clang/utils/analyzer/CmpRuns.py clang/utils/analyzer/SATestBuild.py Removed: diff --git a/clang/utils/analyzer/CmpRuns.py b/clang/utils/analyzer/CmpRuns.py index 5199a87a8205..d80481da03f1 100755 --- a/clang/utils/analyzer/CmpRuns.py +++ b/clang/utils/analyzer/CmpRuns.py @@ -35,8 +35,9 @@ from math import log from collections import defaultdict from copy import copy -from typing import (Any, cast, Dict, List, Optional, Sequence, TextIO, TypeVar, -Tuple, Union) +from enum import Enum +from typing import (Any, cast, Dict, List, NamedTuple, Optional, Sequence, +TextIO, TypeVar, Tuple, Union) Number = Union[int, float] @@ -58,6 +59,17 @@ class Colors: CLEAR = '\x1b[0m' +class HistogramType(str, Enum): +RELATIVE = "relative" +LOG_RELATIVE = "log-relative" +ABSOLUTE = "absolute" + + +class ResultsDirectory(NamedTuple): +path: str +root: str = "" + + class SingleRunInfo: """ Information about analysis run: @@ -65,9 +77,10 @@ class SingleRunInfo: root - the name of the root directory, which will be disregarded when determining the source file name """ -def __init__(self, path: str, root: str = "", verbose_log=None): -self.path = path -self.root = root.rstrip("/\\") +def __init__(self, results: ResultsDirectory, + verbose_log: Optional[str] = None): +self.path = results.path +self.root = results.root.rstrip("/\\") self.verbose_log = verbose_log @@ -232,13 +245,13 @@ def __init__(self, run: AnalysisRun, files: List[str]): self.diagnostics: List[AnalysisDiagnostic] = [] -def load_results(path: str, args: argparse.Namespace, root: str = "", - delete_empty: bool = True) -> AnalysisRun: +def load_results(results: ResultsDirectory, delete_empty: bool = True, + verbose_log: Optional[str] = None) -> AnalysisRun: """ Backwards compatibility API. """ -return load_results_from_single_run(SingleRunInfo(path, root, - args.verbose_log), +return load_results_from_single_run(SingleRunInfo(results, + verbose_log), delete_empty) @@ -280,7 +293,8 @@ def cmp_analysis_diagnostic(d): def compare_results(results_old: AnalysisRun, results_new: AnalysisRun, -args: argparse.Namespace) -> ComparisonResult: +histogram: Optional[HistogramType] = None +) -> ComparisonResult: """ compare_results - Generate a relation from diagnostics in run A to diagnostics in run B. @@ -311,15 +325,15 @@ def compare_results(results_old: AnalysisRun, results_new: AnalysisRun, if a.get_issue_identifier() == b.get_issue_identifier(): if a.get_path_length() != b.get_path_length(): -if args.relative_path_histogram: +if histogram == HistogramType.RELATIVE: path_ diff erence_data.append( float(a.get_path_length()) / b.get_path_length()) -elif args.relative_log_path_histogram: +elif histogram == HistogramType.LOG_RELATIVE: path_ diff erence_data.append( log(float(a.get_path_length()) / b.get_path_length())) -elif args.absolute_path_histogram: +elif histogram == HistogramType.ABSOLUTE: path_ diff erence_data.append( a.get_path_length() - b.get_path_length()) @@ -347,8 +361,7 @@ def compare_results(results_old: AnalysisRun, results_new: AnalysisRun, for b in neq_new: res.append((None, b)) -if args.relative_log_path_histogram or args.relative_path_histogram or \ -args.absolute_path_histogram: +if histogram: from matplotlib import pyplot pyplot.hist(path_ diff erence_data, bins=100) pyplot.show() @@ -394,7 +407,8 @@ def derive_stats(results: AnalysisRun) -> Stats: # TODO: compare_results decouples comparison from the output, we should # do it here as well -def
[clang] 4a7b3d4 - [analyzer] SATestAdd.py: Parse arguments with argparse
Author: Valeriy Savchenko Date: 2020-06-16T13:30:00+03:00 New Revision: 4a7b3d406f1eca8c9bb8091f92da55ff2c72ab44 URL: https://github.com/llvm/llvm-project/commit/4a7b3d406f1eca8c9bb8091f92da55ff2c72ab44 DIFF: https://github.com/llvm/llvm-project/commit/4a7b3d406f1eca8c9bb8091f92da55ff2c72ab44.diff LOG: [analyzer] SATestAdd.py: Parse arguments with argparse Differential Revision: https://reviews.llvm.org/D81565 Added: Modified: clang/utils/analyzer/SATestAdd.py Removed: diff --git a/clang/utils/analyzer/SATestAdd.py b/clang/utils/analyzer/SATestAdd.py index 83ff3d719ea1..7d68ef261dbf 100755 --- a/clang/utils/analyzer/SATestAdd.py +++ b/clang/utils/analyzer/SATestAdd.py @@ -45,18 +45,18 @@ import SATestBuild from ProjectMap import ProjectMap, ProjectInfo +import argparse import os import sys -def add_new_project(name: str, build_mode: int): +def add_new_project(project: ProjectInfo): """ Add a new project for testing: build it and add to the Project Map file. :param name: is a short string used to identify a project. """ -project_info = ProjectInfo(name, build_mode) -test_info = SATestBuild.TestInfo(project_info, +test_info = SATestBuild.TestInfo(project, is_reference_build=True) tester = SATestBuild.ProjectTester(test_info) @@ -71,37 +71,55 @@ def add_new_project(name: str, build_mode: int): # Add the project name to the project map. project_map = ProjectMap(should_exist=False) -if is_existing_project(project_map, name): -print(f"Warning: Project with name '{name}' already exists.", +if is_existing_project(project_map, project): +print(f"Warning: Project with name '{project.name}' already exists.", file=sys.stdout) print("Reference output has been regenerated.", file=sys.stdout) else: -project_map.projects.append(project_info) +project_map.projects.append(project) project_map.save() -def is_existing_project(project_map: ProjectMap, project_name: str) -> bool: -return any(existing_project.name == project_name +def is_existing_project(project_map: ProjectMap, project: ProjectInfo) -> bool: +return any(existing_project.name == project.name for existing_project in project_map.projects) -# TODO: Use argparse # TODO: Add an option not to build. # TODO: Set the path to the Repository directory. if __name__ == "__main__": -if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help"): -print("Add a new project for testing to the analyzer" - "\nUsage: ", sys.argv[0], - "project_ID \n" - "mode: 0 for single file project, " - "1 for scan_build, " - "2 for single file c++11 project", file=sys.stderr) -sys.exit(-1) - -build_mode = 1 -if len(sys.argv) >= 3: -build_mode = int(sys.argv[2]) - -assert((build_mode == 0) | (build_mode == 1) | (build_mode == 2)) - -add_new_project(sys.argv[1], build_mode) +parser = argparse.ArgumentParser() + +parser.add_argument("name", nargs=1, help="Name of the new project") +parser.add_argument("--mode", action="store", default=1, type=int, +choices=[0, 1, 2], +help="Build mode: 0 for single file project, " +"1 for scan_build, " +"2 for single file c++11 project") +parser.add_argument("--source", action="store", default="script", +choices=["script", "git", "zip"], +help=f"Source type of the new project: " +f"'git' for getting from git " +f"(please provide --origin and --commit), " +f"'zip' for unpacking source from a zip file, " +f"'script' for downloading source by running " +f"a custom script {SATestBuild.DOWNLOAD_SCRIPT}") +parser.add_argument("--origin", action="store", default="", +help="Origin link for a git repository") +parser.add_argument("--commit", action="store", default="", +help="Git hash for a commit to checkout") + +args = parser.parse_args() + +if args.source == "git" and (args.origin == "" or args.commit == ""): +parser.error( +"Please provide both --origin and --commit if source is 'git'") + +if args.source != "git" and (args.origin != "" or args.commit != ""): +parser.error("Options --origin and --commit don't make sense when " + "source is not 'git'") + +project = ProjectInfo(args.name[0], args.mode, args.source, args.origin, + args.commit) + +add_new_project(project) ___
[clang] bbb8f17 - [analyzer] SATest: Add posibility to download source from git and zip
Author: Valeriy Savchenko Date: 2020-06-16T13:30:00+03:00 New Revision: bbb8f171364b78c6290fcdbf48b214a870dd1caf URL: https://github.com/llvm/llvm-project/commit/bbb8f171364b78c6290fcdbf48b214a870dd1caf DIFF: https://github.com/llvm/llvm-project/commit/bbb8f171364b78c6290fcdbf48b214a870dd1caf.diff LOG: [analyzer] SATest: Add posibility to download source from git and zip Differential Revision: https://reviews.llvm.org/D81564 Added: Modified: clang/utils/analyzer/ProjectMap.py clang/utils/analyzer/SATestBuild.py Removed: diff --git a/clang/utils/analyzer/ProjectMap.py b/clang/utils/analyzer/ProjectMap.py index 182a05c1a935..5b15e405d26c 100644 --- a/clang/utils/analyzer/ProjectMap.py +++ b/clang/utils/analyzer/ProjectMap.py @@ -1,7 +1,8 @@ import json import os -from typing import Any, Dict, List, NamedTuple, Optional +from enum import Enum +from typing import Any, Dict, List, NamedTuple, Optional, Tuple JSON = Dict[str, Any] @@ -10,12 +11,21 @@ DEFAULT_MAP_FILE = "projects.json" +class DownloadType(str, Enum): +GIT = "git" +ZIP = "zip" +SCRIPT = "script" + + class ProjectInfo(NamedTuple): """ Information about a project to analyze. """ name: str mode: int +source: DownloadType = DownloadType.SCRIPT +origin: str = "" +commit: str = "" enabled: bool = True @@ -73,12 +83,29 @@ def _parse_project(raw_project: JSON) -> ProjectInfo: name: str = raw_project["name"] build_mode: int = raw_project["mode"] enabled: bool = raw_project.get("enabled", True) -return ProjectInfo(name, build_mode, enabled) +source: DownloadType = raw_project.get("source", "zip") + +if source == DownloadType.GIT: +origin, commit = ProjectMap._get_git_params(raw_project) +else: +origin, commit = "", "" + +return ProjectInfo(name, build_mode, source, origin, commit, + enabled) except KeyError as e: raise ValueError( f"Project info is required to have a '{e.args[0]}' field") +@staticmethod +def _get_git_params(raw_project: JSON) -> Tuple[str, str]: +try: +return raw_project["origin"], raw_project["commit"] +except KeyError as e: +raise ValueError( +f"Profect info is required to have a '{e.args[0]}' field " +f"if it has a 'git' source") + @staticmethod def _create_empty(path: str): ProjectMap._save([], path) diff --git a/clang/utils/analyzer/SATestBuild.py b/clang/utils/analyzer/SATestBuild.py index 8b0b80318471..41cb5db44762 100755 --- a/clang/utils/analyzer/SATestBuild.py +++ b/clang/utils/analyzer/SATestBuild.py @@ -44,7 +44,7 @@ """ import CmpRuns import SATestUtils -from ProjectMap import ProjectInfo, ProjectMap +from ProjectMap import DownloadType, ProjectInfo, ProjectMap import argparse import glob @@ -57,6 +57,7 @@ import sys import threading import time +import zipfile from queue import Queue # mypy has problems finding InvalidFileException in the module @@ -198,63 +199,6 @@ def run_cleanup_script(directory: str, build_log_file: IO): verbose=VERBOSE) -def download_and_patch(directory: str, build_log_file: IO): -""" -Download the project and apply the local patchfile if it exists. -""" -cached_source = os.path.join(directory, CACHED_SOURCE_DIR_NAME) - -# If the we don't already have the cached source, run the project's -# download script to download it. -if not os.path.exists(cached_source): -download(directory, build_log_file) -if not os.path.exists(cached_source): -stderr(f"Error: '{cached_source}' not found after download.\n") -exit(1) - -patched_source = os.path.join(directory, PATCHED_SOURCE_DIR_NAME) - -# Remove potentially stale patched source. -if os.path.exists(patched_source): -shutil.rmtree(patched_source) - -# Copy the cached source and apply any patches to the copy. -shutil.copytree(cached_source, patched_source, symlinks=True) -apply_patch(directory, build_log_file) - - -def download(directory: str, build_log_file: IO): -""" -Run the script to download the project, if it exists. -""" -script_path = os.path.join(directory, DOWNLOAD_SCRIPT) -SATestUtils.run_script(script_path, build_log_file, directory, - out=LOCAL.stdout, err=LOCAL.stderr, - verbose=VERBOSE) - - -def apply_patch(directory: str, build_log_file: IO): -patchfile_path = os.path.join(directory, PATCHFILE_NAME) -patched_source = os.path.join(directory, PATCHED_SOURCE_DIR_NAME) - -if not os.path.exists(patchfile_path): -stdout(" No local
[clang] d9944da - [analyzer] SATest: Introduce a single entrypoint for regression scripts
Author: Valeriy Savchenko Date: 2020-06-16T13:30:01+03:00 New Revision: d9944da273c42846947b028c02b7a59880ca50ed URL: https://github.com/llvm/llvm-project/commit/d9944da273c42846947b028c02b7a59880ca50ed DIFF: https://github.com/llvm/llvm-project/commit/d9944da273c42846947b028c02b7a59880ca50ed.diff LOG: [analyzer] SATest: Introduce a single entrypoint for regression scripts Differential Revision: https://reviews.llvm.org/D81567 Added: clang/utils/analyzer/SATest.py Modified: clang/utils/analyzer/CmpRuns.py clang/utils/analyzer/SATestAdd.py clang/utils/analyzer/SATestBuild.py clang/utils/analyzer/SATestUpdateDiffs.py Removed: diff --git a/clang/utils/analyzer/CmpRuns.py b/clang/utils/analyzer/CmpRuns.py old mode 100755 new mode 100644 index d80481da03f1..f7f28d9dc72e --- a/clang/utils/analyzer/CmpRuns.py +++ b/clang/utils/analyzer/CmpRuns.py @@ -25,7 +25,6 @@ diff = compare_results(resultsA, resultsB) """ -import argparse import json import os import plistlib @@ -524,51 +523,7 @@ def dump_scan_build_results_ diff (dir_old: ResultsDirectory, len(results_new.diagnostics) -def generate_option_parser(): -parser = argparse.ArgumentParser() - -parser.add_argument("--root-old", dest="root_old", -help="Prefix to ignore on source files for " -"OLD directory", -action="store", type=str, default="") -parser.add_argument("--root-new", dest="root_new", -help="Prefix to ignore on source files for " -"NEW directory", -action="store", type=str, default="") -parser.add_argument("--verbose-log", dest="verbose_log", -help="Write additional information to LOG " -"[default=None]", -action="store", type=str, default=None, -metavar="LOG") -parser.add_argument("--stats-only", action="store_true", dest="stats_only", -default=False, help="Only show statistics on reports") -parser.add_argument("--show-stats", action="store_true", dest="show_stats", -default=False, help="Show change in statistics") -parser.add_argument("--histogram", action="store", default=None, -choices=[HistogramType.RELATIVE.value, - HistogramType.LOG_RELATIVE.value, - HistogramType.ABSOLUTE.value], -help="Show histogram of paths diff erences. " -"Requires matplotlib") -parser.add_argument("old", nargs=1, help="Directory with old results") -parser.add_argument("new", nargs=1, help="Directory with new results") - -return parser - - -def main(): -parser = generate_option_parser() -args = parser.parse_args() - -dir_old = ResultsDirectory(args.old[0], args.root_old) -dir_new = ResultsDirectory(args.new[0], args.root_new) - -dump_scan_build_results_ diff (dir_old, dir_new, - show_stats=args.show_stats, - stats_only=args.stats_only, - histogram=args.histogram, - verbose_log=args.verbose_log) - - -if __name__ == '__main__': -main() +if __name__ == "__main__": +print("CmpRuns.py should not be used on its own.") +print("Please use 'SATest.py compare' instead") +sys.exit(1) diff --git a/clang/utils/analyzer/SATest.py b/clang/utils/analyzer/SATest.py new file mode 100755 index ..4fb7bf749947 --- /dev/null +++ b/clang/utils/analyzer/SATest.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python + +import SATestAdd +import SATestBuild +import SATestUpdateDiffs +import CmpRuns + +from ProjectMap import ProjectInfo, ProjectMap + +import argparse +import sys + + +def add(parser, args): +if args.source == "git" and (args.origin == "" or args.commit == ""): +parser.error( +"Please provide both --origin and --commit if source is 'git'") + +if args.source != "git" and (args.origin != "" or args.commit != ""): +parser.error("Options --origin and --commit don't make sense when " + "source is not 'git'") + +project = ProjectInfo(args.name[0], args.mode, args.source, args.origin, + args.commit) + +SATestAdd.add_new_project(project) + + +def build(parser, args): +SATestBuild.VERBOSE = args.verbose +tester = SATestBuild.RegressionTester(args.jobs, args.override_compiler, + args.extra_analyzer_config, + args.regenerate, + args.strictness) +tests_passed = tester.test_all() + +if not tests_passed: +
[clang] fb4b565 - [analyzer] SATest: Move from csv to json project maps
Author: Valeriy Savchenko Date: 2020-06-16T13:30:00+03:00 New Revision: fb4b565212b0158e2b41ffec71a7c4282907cda9 URL: https://github.com/llvm/llvm-project/commit/fb4b565212b0158e2b41ffec71a7c4282907cda9 DIFF: https://github.com/llvm/llvm-project/commit/fb4b565212b0158e2b41ffec71a7c4282907cda9.diff LOG: [analyzer] SATest: Move from csv to json project maps Summary: JSON format is a bit more verbose and easier to reason about and extend. For this reason, before extending SATestBuild functionality it is better to refactor the part of how we configure the whole system. Differential Revision: https://reviews.llvm.org/D81563 Added: clang/utils/analyzer/ProjectMap.py Modified: clang/utils/analyzer/SATestAdd.py clang/utils/analyzer/SATestBuild.py clang/utils/analyzer/SATestUpdateDiffs.py Removed: diff --git a/clang/utils/analyzer/ProjectMap.py b/clang/utils/analyzer/ProjectMap.py new file mode 100644 index ..182a05c1a935 --- /dev/null +++ b/clang/utils/analyzer/ProjectMap.py @@ -0,0 +1,94 @@ +import json +import os + +from typing import Any, Dict, List, NamedTuple, Optional + + +JSON = Dict[str, Any] + + +DEFAULT_MAP_FILE = "projects.json" + + +class ProjectInfo(NamedTuple): +""" +Information about a project to analyze. +""" +name: str +mode: int +enabled: bool = True + + +class ProjectMap: +""" +Project map stores info about all the "registered" projects. +""" +def __init__(self, path: Optional[str] = None, should_exist: bool = True): +""" +:param path: optional path to a project JSON file, when None defaults + to DEFAULT_MAP_FILE. +:param should_exist: flag to tell if it's an exceptional situation when + the project file doesn't exist, creates an empty + project list instead if we are not expecting it to + exist. +""" +if path is None: +path = os.path.join(os.path.abspath(os.curdir), DEFAULT_MAP_FILE) + +if not os.path.exists(path): +if should_exist: +raise ValueError( +f"Cannot find the project map file {path}" +f"\nRunning script for the wrong directory?\n") +else: +self._create_empty(path) + +self.path = path +self._load_projects() + +def save(self): +""" +Save project map back to its original file. +""" +self._save(self.projects, self.path) + +def _load_projects(self): +with open(self.path) as raw_data: +raw_projects = json.load(raw_data) + +if not isinstance(raw_projects, list): +raise ValueError( +"Project map should be a list of JSON objects") + +self.projects = self._parse(raw_projects) + +@staticmethod +def _parse(raw_projects: List[JSON]) -> List[ProjectInfo]: +return [ProjectMap._parse_project(raw_project) +for raw_project in raw_projects] + +@staticmethod +def _parse_project(raw_project: JSON) -> ProjectInfo: +try: +name: str = raw_project["name"] +build_mode: int = raw_project["mode"] +enabled: bool = raw_project.get("enabled", True) +return ProjectInfo(name, build_mode, enabled) + +except KeyError as e: +raise ValueError( +f"Project info is required to have a '{e.args[0]}' field") + +@staticmethod +def _create_empty(path: str): +ProjectMap._save([], path) + +@staticmethod +def _save(projects: List[ProjectInfo], path: str): +with open(path, "w") as output: +json.dump(ProjectMap._convert_infos_to_dicts(projects), + output, indent=2) + +@staticmethod +def _convert_infos_to_dicts(projects: List[ProjectInfo]) -> List[JSON]: +return [project._asdict() for project in projects] diff --git a/clang/utils/analyzer/SATestAdd.py b/clang/utils/analyzer/SATestAdd.py index 012d8ec3fd9a..83ff3d719ea1 100755 --- a/clang/utils/analyzer/SATestAdd.py +++ b/clang/utils/analyzer/SATestAdd.py @@ -43,13 +43,11 @@ > changes_for_analyzer.patch """ import SATestBuild +from ProjectMap import ProjectMap, ProjectInfo -import csv import os import sys -from typing import IO - def add_new_project(name: str, build_mode: int): """ @@ -57,9 +55,10 @@ def add_new_project(name: str, build_mode: int): :param name: is a short string used to identify a project. """ -project_info = SATestBuild.ProjectInfo(name, build_mode, - is_reference_build=True) -tester = SATestBuild.ProjectTester(project_info) +project_info = ProjectInfo(name, build_mode)
[clang] dc8a77d - [analyzer] ProjectMap: Do not serialize fields with default values
Author: Valeriy Savchenko Date: 2020-06-16T13:30:01+03:00 New Revision: dc8a77de7db71dc52b0c75b3bb5437d9ae0ccc8c URL: https://github.com/llvm/llvm-project/commit/dc8a77de7db71dc52b0c75b3bb5437d9ae0ccc8c DIFF: https://github.com/llvm/llvm-project/commit/dc8a77de7db71dc52b0c75b3bb5437d9ae0ccc8c.diff LOG: [analyzer] ProjectMap: Do not serialize fields with default values Differential Revision: https://reviews.llvm.org/D81568 Added: Modified: clang/utils/analyzer/ProjectMap.py Removed: diff --git a/clang/utils/analyzer/ProjectMap.py b/clang/utils/analyzer/ProjectMap.py index 5b15e405d26c..62b7e48fde9b 100644 --- a/clang/utils/analyzer/ProjectMap.py +++ b/clang/utils/analyzer/ProjectMap.py @@ -118,4 +118,17 @@ def _save(projects: List[ProjectInfo], path: str): @staticmethod def _convert_infos_to_dicts(projects: List[ProjectInfo]) -> List[JSON]: -return [project._asdict() for project in projects] +return [ProjectMap._convert_info_to_dict(project) +for project in projects] + +@staticmethod +def _convert_info_to_dict(project: ProjectInfo) -> JSON: +whole_dict = project._asdict() +defaults = project._field_defaults + +# there is no need in serializing fields with default values +for field, default_value in defaults.items(): +if whole_dict[field] == default_value: +del whole_dict[field] + +return whole_dict ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 29353e6 - [analyzer] LoopWidening: fix crash by avoiding aliased references invalidation
Author: Abbas Sabra Date: 2020-06-09T12:55:54+03:00 New Revision: 29353e69d25c0f13cd2704ce2269af464d0751a8 URL: https://github.com/llvm/llvm-project/commit/29353e69d25c0f13cd2704ce2269af464d0751a8 DIFF: https://github.com/llvm/llvm-project/commit/29353e69d25c0f13cd2704ce2269af464d0751a8.diff LOG: [analyzer] LoopWidening: fix crash by avoiding aliased references invalidation Summary: LoopWidening is invalidating references coming from type aliases which lead to a crash. Patch by Abbas Sabra! Differential Revision: https://reviews.llvm.org/D80669 Added: Modified: clang/lib/StaticAnalyzer/Core/LoopWidening.cpp clang/test/Analysis/loop-widening-preserve-reference-type.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp b/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp index 9a7b1a24b819..47e34dd84b9a 100644 --- a/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp +++ b/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp @@ -67,8 +67,10 @@ ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState, } // References should not be invalidated. - auto Matches = match(findAll(stmt(hasDescendant(varDecl(hasType(referenceType())).bind(MatchRef, - *LCtx->getDecl()->getBody(), ASTCtx); + auto Matches = match( + findAll(stmt(hasDescendant( + varDecl(hasType(hasCanonicalType(referenceType(.bind(MatchRef, + *LCtx->getDecl()->getBody(), ASTCtx); for (BoundNodes Match : Matches) { const VarDecl *VD = Match.getNodeAs(MatchRef); assert(VD); diff --git a/clang/test/Analysis/loop-widening-preserve-reference-type.cpp b/clang/test/Analysis/loop-widening-preserve-reference-type.cpp index b5746d1fe767..38dcb4fbb6ac 100644 --- a/clang/test/Analysis/loop-widening-preserve-reference-type.cpp +++ b/clang/test/Analysis/loop-widening-preserve-reference-type.cpp @@ -12,3 +12,11 @@ void invalid_type_region_access() { for (int i = 0; i < 10; ++i) { } clang_analyzer_eval( != 0); // expected-warning{{TRUE}} } // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to true}} + +using AR = const A &; +void invalid_type_alias_region_access() { + AR x = B(); + for (int i = 0; i < 10; ++i) { + } + clang_analyzer_eval( != 0); // expected-warning{{TRUE}} +} // expected-warning@-1{{reference cannot be bound to dereferenced null pointer in well-defined C++ code; comparison may be assumed to always evaluate to true}} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 5395389 - [analyzer] SATestBuild.py: Make verbosity level a cmd option
Author: Valeriy Savchenko Date: 2020-05-28T20:47:06+03:00 New Revision: 5395389475bcaba16966ab62125f2f54ea81c915 URL: https://github.com/llvm/llvm-project/commit/5395389475bcaba16966ab62125f2f54ea81c915 DIFF: https://github.com/llvm/llvm-project/commit/5395389475bcaba16966ab62125f2f54ea81c915.diff LOG: [analyzer] SATestBuild.py: Make verbosity level a cmd option Reviewers: NoQ, dcoughlin Subscribers: xazax.hun, baloghadamsoftware, szepet, a.sidorin, mikhail.ramalho, Szelethus, donat.nagy, dkrupp, Charusso, ASDenysPetrov, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D80626 Added: Modified: clang/utils/analyzer/SATestBuild.py Removed: diff --git a/clang/utils/analyzer/SATestBuild.py b/clang/utils/analyzer/SATestBuild.py index e2fe6a95138b..5ff430d5fcf3 100755 --- a/clang/utils/analyzer/SATestBuild.py +++ b/clang/utils/analyzer/SATestBuild.py @@ -389,7 +389,7 @@ def test(self) -> bool: start_time = time.time() project_dir = self.get_project_dir() -if VERBOSE == 1: +if VERBOSE >= 1: stdout(f" Build directory: {project_dir}.\n") # Set the build results directory. @@ -431,7 +431,7 @@ def build(self, directory: str, output_dir: str): # Clean up scan build results. if os.path.exists(output_dir): -if VERBOSE == 1: +if VERBOSE >= 1: stdout(f" Removing old results: {output_dir}\n") shutil.rmtree(output_dir) @@ -517,7 +517,7 @@ def scan_build(self, directory: str, output_dir: str, build_log_file: IO): command_to_run = command_prefix + command -if VERBOSE == 1: +if VERBOSE >= 1: stdout(f" Executing: {command_to_run}\n") check_call(command_to_run, cwd=cwd, @@ -575,7 +575,7 @@ def analyze_preprocessed(self, directory: str, output_dir: str): log_path = os.path.join(fail_path, file_name + ".stderr.txt") with open(log_path, "w+") as log_file: try: -if VERBOSE == 1: +if VERBOSE >= 1: stdout(f" Executing: {command}\n") check_call(command, cwd=directory, stderr=log_file, @@ -744,7 +744,7 @@ def run_cmp_results(directory: str, strictness: int = 0) -> bool: for ref_dir, new_dir in zip(ref_list, new_list): assert(ref_dir != new_dir) -if VERBOSE == 1: +if VERBOSE >= 1: stdout(f" Comparing Results: {ref_dir} {new_dir}\n") patched_source = os.path.join(directory, PATCHED_SOURCE_DIR_NAME) @@ -818,7 +818,7 @@ def remove_log_file(output_dir: str): # Clean up the log file. if os.path.exists(build_log_path): -if VERBOSE == 1: +if VERBOSE >= 1: stdout(f" Removing log file: {build_log_path}\n") os.remove(build_log_path) @@ -887,29 +887,31 @@ def validate_project_file(map_file: IO): if __name__ == "__main__": # Parse command line arguments. -Parser = argparse.ArgumentParser( +parser = argparse.ArgumentParser( description="Test the Clang Static Analyzer.") -Parser.add_argument("--strictness", dest="strictness", type=int, default=0, +parser.add_argument("--strictness", dest="strictness", type=int, default=0, help="0 to fail on runtime errors, 1 to fail when the " "number of found bugs are diff erent from the " "reference, 2 to fail on any diff erence from the " "reference. Default is 0.") -Parser.add_argument("-r", dest="regenerate", action="store_true", +parser.add_argument("-r", dest="regenerate", action="store_true", default=False, help="Regenerate reference output.") -Parser.add_argument("--override-compiler", action="store_true", +parser.add_argument("--override-compiler", action="store_true", default=False, help="Call scan-build with " "--override-compiler option.") -Parser.add_argument("-j", "--jobs", dest="jobs", type=int, +parser.add_argument("-j", "--jobs", dest="jobs", type=int, default=0, help="Number of projects to test concurrently") -Parser.add_argument("--extra-analyzer-config", +parser.add_argument("--extra-analyzer-config", dest="extra_analyzer_config", type=str, default="", help="Arguments passed to to -analyzer-config") +parser.add_argument("-v", "--verbose", action="count", default=0) -args = Parser.parse_args() +args = parser.parse_args() +VERBOSE = args.verbose tester = RegressionTester(args.jobs, args.override_compiler,
[clang] 116dcbe - [analyzer] Remove unused function declaration. NFC.
Author: Valeriy Savchenko Date: 2020-05-28T20:28:17+03:00 New Revision: 116dcbebc6a1648b4acd1a1a391c1d66a3eb4b5f URL: https://github.com/llvm/llvm-project/commit/116dcbebc6a1648b4acd1a1a391c1d66a3eb4b5f DIFF: https://github.com/llvm/llvm-project/commit/116dcbebc6a1648b4acd1a1a391c1d66a3eb4b5f.diff LOG: [analyzer] Remove unused function declaration. NFC. Added: Modified: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 0822a9461fa7..a14b29c6face 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -801,7 +801,6 @@ class RangeConstraintManager : public RangedConstraintManager { RangeSet::Factory F; RangeSet getRange(ProgramStateRef State, SymbolRef Sym); - const RangeSet *getRangeForMinusSymbol(ProgramStateRef State, SymbolRef Sym); RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt , ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] db3b970 - [analyzer] Remove unused function. NFC.
Author: Valeriy Savchenko Date: 2020-05-28T20:22:50+03:00 New Revision: db3b970a84325e326bbcec4bc3e5e663f148a481 URL: https://github.com/llvm/llvm-project/commit/db3b970a84325e326bbcec4bc3e5e663f148a481 DIFF: https://github.com/llvm/llvm-project/commit/db3b970a84325e326bbcec4bc3e5e663f148a481.diff LOG: [analyzer] Remove unused function. NFC. Added: Modified: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 6f92b965ce5b..0822a9461fa7 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -929,30 +929,6 @@ RangeSet RangeConstraintManager::getRange(ProgramStateRef State, return SymbolicRangeInferrer::inferRange(getBasicVals(), F, State, Sym); } -// FIXME: Once SValBuilder supports unary minus, we should use SValBuilder to -//obtain the negated symbolic expression instead of constructing the -//symbol manually. This will allow us to support finding ranges of not -//only negated SymSymExpr-type expressions, but also of other, simpler -//expressions which we currently do not know how to negate. -const RangeSet* -RangeConstraintManager::getRangeForMinusSymbol(ProgramStateRef State, - SymbolRef Sym) { - if (const SymSymExpr *SSE = dyn_cast(Sym)) { -if (SSE->getOpcode() == BO_Sub) { - QualType T = Sym->getType(); - SymbolManager = State->getSymbolManager(); - SymbolRef negSym = SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, - SSE->getLHS(), T); - if (const RangeSet *negV = State->get(negSym)) { -if (T->isUnsignedIntegerOrEnumerationType() || -T->isSignedIntegerOrEnumerationType()) - return negV; - } -} - } - return nullptr; -} - //====== // assumeSymX methods: protected interface for RangeConstraintManager. //======/ ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 2a09daf - [analyzer] Generalize bitwise AND rules for ranges
Author: Valeriy Savchenko Date: 2020-05-28T18:55:49+03:00 New Revision: 2a09daff0f902e70a08f2b30f3461fb8848f5ab1 URL: https://github.com/llvm/llvm-project/commit/2a09daff0f902e70a08f2b30f3461fb8848f5ab1 DIFF: https://github.com/llvm/llvm-project/commit/2a09daff0f902e70a08f2b30f3461fb8848f5ab1.diff LOG: [analyzer] Generalize bitwise AND rules for ranges Summary: Previously the current solver started reasoning about bitwise AND expressions only when one of the operands is a constant. However, very similar logic could be applied to ranges. This commit addresses this shortcoming. Additionally, it refines how we deal with negative operands. rdar://problem/54359410 Differential Revision: https://reviews.llvm.org/D79434 Added: clang/test/Analysis/uninit-exhaustive-switch-bug.c Modified: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp clang/test/Analysis/constant-folding.c clang/test/Analysis/switch-case.c Removed: diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 570161543805..b73c395d80fa 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -430,9 +430,9 @@ class SymbolicRangeInferrer RangeSet RHS, QualType T) { switch (Op) { case BO_Or: - return VisitOrOperator(LHS, RHS, T); + return VisitBinaryOperator(LHS, RHS, T); case BO_And: - return VisitAndOperator(LHS, RHS, T); + return VisitBinaryOperator(LHS, RHS, T); default: return infer(T); } @@ -464,19 +464,19 @@ class SymbolicRangeInferrer ValueFactory.Convert(To, Origin.To())); } - RangeSet VisitOrOperator(RangeSet LHS, RangeSet RHS, QualType T) { + template + RangeSet VisitBinaryOperator(RangeSet LHS, RangeSet RHS, QualType T) { // We should propagate information about unfeasbility of one of the // operands to the resulting range. if (LHS.isEmpty() || RHS.isEmpty()) { return RangeFactory.getEmptySet(); } -APSIntType ResultType = ValueFactory.getAPSIntType(T); -RangeSet DefaultRange = infer(T); - Range CoarseLHS = fillGaps(LHS); Range CoarseRHS = fillGaps(RHS); +APSIntType ResultType = ValueFactory.getAPSIntType(T); + // We need to convert ranges to the resulting type, so we can compare values // and combine them in a meaningful (in terms of the given operation) way. auto ConvertedCoarseLHS = convert(CoarseLHS, ResultType); @@ -485,74 +485,14 @@ class SymbolicRangeInferrer // It is hard to reason about ranges when conversion changes // borders of the ranges. if (!ConvertedCoarseLHS || !ConvertedCoarseRHS) { - return DefaultRange; -} - -llvm::APSInt Zero = ResultType.getZeroValue(); - -bool IsLHSPositiveOrZero = ConvertedCoarseLHS->From() >= Zero; -bool IsRHSPositiveOrZero = ConvertedCoarseRHS->From() >= Zero; - -bool IsLHSNegative = ConvertedCoarseLHS->To() < Zero; -bool IsRHSNegative = ConvertedCoarseRHS->To() < Zero; - -// Check if both ranges have the same sign. -if ((IsLHSPositiveOrZero && IsRHSPositiveOrZero) || -(IsLHSNegative && IsRHSNegative)) { - // The result is definitely greater or equal than any of the operands. - const llvm::APSInt = - std::max(ConvertedCoarseLHS->From(), ConvertedCoarseRHS->From()); - - // We estimate maximal value for positives as the maximal value for the - // given type. For negatives, we estimate it with -1 (e.g. 0x). - // - // TODO: We basically, limit the resulting range from below (in absolute - // numbers), but don't do anything with the upper bound. - // For positive operands, it can be done as follows: for the upper - // bound of LHS and RHS we calculate the most significant bit set. - // Let's call it the N-th bit. Then we can estimate the maximal - // number to be 2^(N+1)-1, i.e. the number with all the bits up to - // the N-th bit set. - const llvm::APSInt = IsLHSNegative -? ValueFactory.getValue(--Zero) -: ValueFactory.getMaxValue(ResultType); - - return {RangeFactory, ValueFactory.getValue(Min), Max}; -} - -// Otherwise, let's check if at least one of the operands is negative. -if (IsLHSNegative || IsRHSNegative) { - // This means that the result is definitely negative as well. - return {RangeFactory, ValueFactory.getMinValue(ResultType), - ValueFactory.getValue(--Zero)}; -} - -// It is pretty hard to reason about operands with diff erent signs -// (and especially with possibly diff erent signs). We simply check if it -// can be zero.
[clang] 73c120a - [analyzer] Introduce reasoning about symbolic remainder operator
Author: Valeriy Savchenko Date: 2020-05-28T18:56:38+03:00 New Revision: 73c120a9895a7e12e3c29a755d64096c8bd0220f URL: https://github.com/llvm/llvm-project/commit/73c120a9895a7e12e3c29a755d64096c8bd0220f DIFF: https://github.com/llvm/llvm-project/commit/73c120a9895a7e12e3c29a755d64096c8bd0220f.diff LOG: [analyzer] Introduce reasoning about symbolic remainder operator Summary: New logic tries to narrow possible result values of the remainder operation based on its operands and their ranges. It also tries to be conservative with negative operands because according to the standard the sign of the result is implementation-defined. rdar://problem/44978988 Differential Revision: https://reviews.llvm.org/D80117 Added: clang/test/Analysis/PR35418.cpp clang/test/Analysis/uninit-bug-first-iteration-init.c Modified: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp clang/test/Analysis/constant-folding.c clang/test/Analysis/hangs.c Removed: diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index b73c395d80fa..6f92b965ce5b 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -433,6 +433,8 @@ class SymbolicRangeInferrer return VisitBinaryOperator(LHS, RHS, T); case BO_And: return VisitBinaryOperator(LHS, RHS, T); +case BO_Rem: + return VisitBinaryOperator(LHS, RHS, T); default: return infer(T); } @@ -496,6 +498,46 @@ class SymbolicRangeInferrer return infer(T); } + /// Return a symmetrical range for the given range and type. + /// + /// If T is signed, return the smallest range [-x..x] that covers the original + /// range, or [-min(T), max(T)] if the aforementioned symmetric range doesn't + /// exist due to original range covering min(T)). + /// + /// If T is unsigned, return the smallest range [0..x] that covers the + /// original range. + Range getSymmetricalRange(Range Origin, QualType T) { +APSIntType RangeType = ValueFactory.getAPSIntType(T); + +if (RangeType.isUnsigned()) { + return Range(ValueFactory.getMinValue(RangeType), Origin.To()); +} + +if (Origin.From().isMinSignedValue()) { + // If mini is a minimal signed value, absolute value of it is greater + // than the maximal signed value. In order to avoid these + // complications, we simply return the whole range. + return {ValueFactory.getMinValue(RangeType), + ValueFactory.getMaxValue(RangeType)}; +} + +// At this point, we are sure that the type is signed and we can safely +// use unary - operator. +// +// While calculating absolute maximum, we can use the following formula +// because of these reasons: +// * If From >= 0 then To >= From and To >= -From. +// AbsMax == To == max(To, -From) +// * If To <= 0 then -From >= -To and -From >= From. +// AbsMax == -From == max(-From, To) +// * Otherwise, From <= 0, To >= 0, and +// AbsMax == max(abs(From), abs(To)) +llvm::APSInt AbsMax = std::max(-Origin.From(), Origin.To()); + +// Intersection is guaranteed to be non-empty. +return {ValueFactory.getValue(-AbsMax), ValueFactory.getValue(AbsMax)}; + } + /// Return a range set subtracting zero from \p Domain. RangeSet assumeNonZero(RangeSet Domain, QualType T) { APSIntType IntType = ValueFactory.getAPSIntType(T); @@ -635,6 +677,63 @@ RangeSet SymbolicRangeInferrer::VisitBinaryOperator(Range LHS, return infer(T); } +template <> +RangeSet SymbolicRangeInferrer::VisitBinaryOperator(Range LHS, +Range RHS, +QualType T) { + llvm::APSInt Zero = ValueFactory.getAPSIntType(T).getZeroValue(); + + Range ConservativeRange = getSymmetricalRange(RHS, T); + + llvm::APSInt Max = ConservativeRange.To(); + llvm::APSInt Min = ConservativeRange.From(); + + if (Max == Zero) { +// It's an undefined behaviour to divide by 0 and it seems like we know +// for sure that RHS is 0. Let's say that the resulting range is +// simply infeasible for that matter. +return RangeFactory.getEmptySet(); + } + + // At this point, our conservative range is closed. The result, however, + // couldn't be greater than the RHS' maximal absolute value. Because of + // this reason, we turn the range into open (or half-open in case of + // unsigned integers). + // + // While we operate on integer values, an open interval (a, b) can be easily + // represented by the closed interval [a + 1, b - 1]. And this is exactly + // what we do next. + // + // If we are dealing with unsigned case, we shouldn't move the
[clang] 47c4b8b - [analyzer] Generalize bitwise OR rules for ranges
Author: Valeriy Savchenko Date: 2020-05-28T18:55:22+03:00 New Revision: 47c4b8bd68698b1827f39c3056783ed042faf718 URL: https://github.com/llvm/llvm-project/commit/47c4b8bd68698b1827f39c3056783ed042faf718 DIFF: https://github.com/llvm/llvm-project/commit/47c4b8bd68698b1827f39c3056783ed042faf718.diff LOG: [analyzer] Generalize bitwise OR rules for ranges Summary: Previously the current solver started reasoning about bitwise OR expressions only when one of the operands is a constant. However, very similar logic could be applied to ranges. This commit addresses this shortcoming. Additionally, it refines how we deal with negative operands. Differential Revision: https://reviews.llvm.org/D79336 Added: Modified: clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp clang/test/Analysis/constant-folding.c Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h index ac218bc070e9..a001c0dc7030 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h @@ -157,6 +157,10 @@ class BasicValueFactory { const llvm::APSInt (QualType T, const llvm::APSInt ) { APSIntType TargetType = getAPSIntType(T); +return Convert(TargetType, From); + } + + const llvm::APSInt (APSIntType TargetType, const llvm::APSInt ) { if (TargetType == APSIntType(From)) return From; @@ -177,11 +181,19 @@ class BasicValueFactory { } const llvm::APSInt (QualType T) { -return getValue(getAPSIntType(T).getMaxValue()); +return getMaxValue(getAPSIntType(T)); } const llvm::APSInt (QualType T) { -return getValue(getAPSIntType(T).getMinValue()); +return getMinValue(getAPSIntType(T)); + } + + const llvm::APSInt (APSIntType T) { +return getValue(T.getMaxValue()); + } + + const llvm::APSInt (APSIntType T) { +return getValue(T.getMinValue()); } const llvm::APSInt (const llvm::APSInt ) { diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h index 97c9c6d63eb2..a42eebd7d4e8 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h @@ -107,14 +107,17 @@ class RangeSet { return ranges.isSingleton() ? ranges.begin()->getConcreteValue() : nullptr; } + /// Get a minimal value covered by the ranges in the set + const llvm::APSInt () const; + /// Get a maximal value covered by the ranges in the set + const llvm::APSInt () const; + private: void IntersectInRange(BasicValueFactory , Factory , const llvm::APSInt , const llvm::APSInt , PrimRangeSet , PrimRangeSet::iterator , PrimRangeSet::iterator ) const; - const llvm::APSInt () const; - bool pin(llvm::APSInt , llvm::APSInt ) const; public: @@ -131,7 +134,6 @@ class RangeSet { } }; - class ConstraintRange {}; using ConstraintRangeTy = llvm::ImmutableMap; diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 368324d3d34f..570161543805 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -69,7 +69,19 @@ void RangeSet::IntersectInRange(BasicValueFactory , Factory , const llvm::APSInt ::getMinValue() const { assert(!isEmpty()); - return ranges.begin()->From(); + return begin()->From(); +} + +const llvm::APSInt ::getMaxValue() const { + assert(!isEmpty()); + // NOTE: It's a shame that we can't implement 'getMaxValue' without scanning + // the whole tree to get to the last element. + // llvm::ImmutableSet should support decrement for 'end' iterators + // or reverse order iteration. + auto It = begin(); + for (auto End = end(); std::next(It) != End; ++It) { + } + return It->To(); } bool RangeSet::pin(llvm::APSInt , llvm::APSInt ) const { @@ -426,22 +438,106 @@ class SymbolicRangeInferrer } } + //===--===// + // Ranges and operators + //===--===// + + /// Return a rough approximation of the given range set. + /// + /// For the range set: + /// { [x_0, y_0], [x_1, y_1], ... , [x_N, y_N] } + /// it will return the
[clang] 1f57d76 - [analyzer] Refactor range inference for symbolic expressions
Author: Valeriy Savchenko Date: 2020-05-28T18:54:52+03:00 New Revision: 1f57d76a8dd00611aaa4b33048be195ea9a2dc44 URL: https://github.com/llvm/llvm-project/commit/1f57d76a8dd00611aaa4b33048be195ea9a2dc44 DIFF: https://github.com/llvm/llvm-project/commit/1f57d76a8dd00611aaa4b33048be195ea9a2dc44.diff LOG: [analyzer] Refactor range inference for symbolic expressions Summary: This change introduces a new component to unite all of the reasoning we have about operations on ranges in the analyzer's solver. In many cases, we might conclude that the range for a symbolic operation is much more narrow than the type implies. While reasoning about runtime conditions (especially in loops), we need to support more and more of those little pieces of logic. The new component mostly plays a role of an organizer for those, and allows us to focus on the actual reasoning about ranges and not dispatching manually on the types of the nested symbolic expressions. Differential Revision: https://reviews.llvm.org/D79232 Added: clang/test/Analysis/double-ranges-bug.c Modified: clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp clang/test/Analysis/constant-folding.c Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h index c72f8292647d..97c9c6d63eb2 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h @@ -30,6 +30,10 @@ class Range : public std::pair { : std::pair(, ) { assert(from <= to); } + + Range(const llvm::APSInt ) + : std::pair(, ) {} + bool Includes(const llvm::APSInt ) const { return *first <= v && v <= *second; } @@ -89,6 +93,9 @@ class RangeSet { RangeSet(Factory , const llvm::APSInt , const llvm::APSInt ) : ranges(F.add(F.getEmptySet(), Range(from, to))) {} + /// Construct a new RangeSet representing the given point as a range. + RangeSet(Factory , const llvm::APSInt ) : RangeSet(F, point, point) {} + /// Profile - Generates a hash profile of this RangeSet for use /// by FoldingSet. void Profile(llvm::FoldingSetNodeID ) const { ranges.Profile(ID); } diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index a3ea7d4c013b..368324d3d34f 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -16,6 +16,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableSet.h" #include "llvm/Support/raw_ostream.h" @@ -23,10 +24,16 @@ using namespace clang; using namespace ento; +//===--===// +// RangeSet implementation +//===--===// + void RangeSet::IntersectInRange(BasicValueFactory , Factory , - const llvm::APSInt , const llvm::APSInt , - PrimRangeSet , PrimRangeSet::iterator , - PrimRangeSet::iterator ) const { +const llvm::APSInt , +const llvm::APSInt , +PrimRangeSet , +PrimRangeSet::iterator , +PrimRangeSet::iterator ) const { // There are six cases for each range R in the set: // 1. R is entirely before the intersection range. // 2. R is entirely after the intersection range. @@ -66,6 +73,11 @@ const llvm::APSInt ::getMinValue() const { } bool RangeSet::pin(llvm::APSInt , llvm::APSInt ) const { + if (isEmpty()) { +// This range is already infeasible. +return false; + } + // This function has nine cases, the cartesian product of range-testing // both the upper and lower bounds against the symbol's type. // Each case requires a diff erent pinning operation. @@ -283,6 +295,207 @@ void RangeSet::print(raw_ostream ) const { } namespace { + +/// A little component aggregating all of the reasoning we have about +/// the ranges of symbolic expressions. +/// +/// Even when we don't know the exact values of the operands, we still +/// can get a pretty good estimate of the result's range. +class SymbolicRangeInferrer +: public SymExprVisitor { +public:
[clang] bb2ae74 - [analyzer] Merge implementations of SymInt, IntSym, and SymSym exprs
Author: Valeriy Savchenko Date: 2020-05-28T18:54:27+03:00 New Revision: bb2ae74717a25ba268e7bd17a2a572d931ed094e URL: https://github.com/llvm/llvm-project/commit/bb2ae74717a25ba268e7bd17a2a572d931ed094e DIFF: https://github.com/llvm/llvm-project/commit/bb2ae74717a25ba268e7bd17a2a572d931ed094e.diff LOG: [analyzer] Merge implementations of SymInt, IntSym, and SymSym exprs Summary: SymIntExpr, IntSymExpr, and SymSymExpr share a big portion of logic that used to be duplicated across all three classes. New implementation also adds an easy way of introducing another type of operands into the mix. Differential Revision: https://reviews.llvm.org/D79156 Added: Modified: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h clang/lib/StaticAnalyzer/Core/SymbolManager.cpp Removed: diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h index 2c505995bee0..390ced8c29f8 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -326,136 +326,83 @@ class BinarySymExpr : public SymExpr { Kind k = SE->getKind(); return k >= BEGIN_BINARYSYMEXPRS && k <= END_BINARYSYMEXPRS; } -}; - -/// Represents a symbolic expression like 'x' + 3. -class SymIntExpr : public BinarySymExpr { - const SymExpr *LHS; - const llvm::APSInt& RHS; -public: - SymIntExpr(const SymExpr *lhs, BinaryOperator::Opcode op, - const llvm::APSInt , QualType t) - : BinarySymExpr(SymIntExprKind, op, t), LHS(lhs), RHS(rhs) { -assert(lhs); +protected: + static unsigned computeOperandComplexity(const SymExpr *Value) { +return Value->computeComplexity(); } - - void dumpToStream(raw_ostream ) const override; - - const SymExpr *getLHS() const { return LHS; } - const llvm::APSInt () const { return RHS; } - - unsigned computeComplexity() const override { -if (Complexity == 0) - Complexity = 1 + LHS->computeComplexity(); -return Complexity; + static unsigned computeOperandComplexity(const llvm::APSInt ) { +return 1; } - static void Profile(llvm::FoldingSetNodeID& ID, const SymExpr *lhs, - BinaryOperator::Opcode op, const llvm::APSInt& rhs, - QualType t) { -ID.AddInteger((unsigned) SymIntExprKind); -ID.AddPointer(lhs); -ID.AddInteger(op); -ID.AddPointer(); -ID.Add(t); + static const llvm::APSInt *getPointer(const llvm::APSInt ) { +return } + static const SymExpr *getPointer(const SymExpr *Value) { return Value; } - void Profile(llvm::FoldingSetNodeID& ID) override { -Profile(ID, LHS, getOpcode(), RHS, getType()); - } - - // Implement isa support. - static bool classof(const SymExpr *SE) { -return SE->getKind() == SymIntExprKind; - } + static void dumpToStreamImpl(raw_ostream , const SymExpr *Value); + static void dumpToStreamImpl(raw_ostream , const llvm::APSInt ); + static void dumpToStreamImpl(raw_ostream , BinaryOperator::Opcode op); }; -/// Represents a symbolic expression like 3 - 'x'. -class IntSymExpr : public BinarySymExpr { - const llvm::APSInt& LHS; - const SymExpr *RHS; +/// Template implementation for all binary symbolic expressions +template +class BinarySymExprImpl : public BinarySymExpr { + LHSTYPE LHS; + RHSTYPE RHS; public: - IntSymExpr(const llvm::APSInt , BinaryOperator::Opcode op, - const SymExpr *rhs, QualType t) - : BinarySymExpr(IntSymExprKind, op, t), LHS(lhs), RHS(rhs) { -assert(rhs); + BinarySymExprImpl(LHSTYPE lhs, BinaryOperator::Opcode op, RHSTYPE rhs, +QualType t) + : BinarySymExpr(ClassKind, op, t), LHS(lhs), RHS(rhs) { +assert(getPointer(lhs)); +assert(getPointer(rhs)); } - void dumpToStream(raw_ostream ) const override; + void dumpToStream(raw_ostream ) const override { +dumpToStreamImpl(os, LHS); +dumpToStreamImpl(os, getOpcode()); +dumpToStreamImpl(os, RHS); + } - const SymExpr *getRHS() const { return RHS; } - const llvm::APSInt () const { return LHS; } + LHSTYPE getLHS() const { return LHS; } + RHSTYPE getRHS() const { return RHS; } unsigned computeComplexity() const override { if (Complexity == 0) - Complexity = 1 + RHS->computeComplexity(); + Complexity = + computeOperandComplexity(RHS) + computeOperandComplexity(LHS); return Complexity; } - static void Profile(llvm::FoldingSetNodeID& ID, const llvm::APSInt& lhs, - BinaryOperator::Opcode op, const SymExpr *rhs, - QualType t) { -ID.AddInteger((unsigned) IntSymExprKind); -ID.AddPointer(); + static void Profile(llvm::FoldingSetNodeID , LHSTYPE lhs, + BinaryOperator::Opcode op, RHSTYPE rhs,
[clang] bd06c41 - [analyzer] Allow bindings of the CompoundLiteralRegion
Author: Valeriy Savchenko Date: 2020-05-28T14:11:57+03:00 New Revision: bd06c417e6c717cbe33b566d7bbaf27fb47e763a URL: https://github.com/llvm/llvm-project/commit/bd06c417e6c717cbe33b566d7bbaf27fb47e763a DIFF: https://github.com/llvm/llvm-project/commit/bd06c417e6c717cbe33b566d7bbaf27fb47e763a.diff LOG: [analyzer] Allow bindings of the CompoundLiteralRegion Summary: CompoundLiteralRegions have been properly modeled before, but 'getBindingForElement` was not changed to accommodate this change properly. rdar://problem/46144644 Differential Revision: https://reviews.llvm.org/D78990 Added: clang/test/Analysis/retain-release-compound-literal.m Modified: clang/lib/StaticAnalyzer/Core/RegionStore.cpp clang/test/Analysis/compound-literals.c clang/unittests/StaticAnalyzer/StoreTest.cpp Removed: diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp index 2a55c9964712..57fde32bc01d 100644 --- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -1628,10 +1628,6 @@ RegionStoreManager::findLazyBinding(RegionBindingsConstRef B, SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, const ElementRegion* R) { - // We do not currently model bindings of the CompoundLiteralregion. - if (isa(R->getBaseRegion())) -return UnknownVal(); - // Check if the region has a binding. if (const Optional = B.getDirectBinding(R)) return *V; diff --git a/clang/test/Analysis/compound-literals.c b/clang/test/Analysis/compound-literals.c index f8b9121494c1..42e6a55a30c7 100644 --- a/clang/test/Analysis/compound-literals.c +++ b/clang/test/Analysis/compound-literals.c @@ -1,4 +1,7 @@ -// RUN: %clang_cc1 -triple=i386-apple-darwin10 -analyze -analyzer-checker=debug.ExprInspection -verify %s +// RUN: %clang_cc1 -triple=i386-apple-darwin10 -verify %s -analyze \ +// RUN: -analyzer-checker=debug.ExprInspection + +#define NULL 0 void clang_analyzer_eval(int); // pr28449: Used to crash. @@ -6,3 +9,15 @@ void foo(void) { static const unsigned short array[] = (const unsigned short[]){0x0F00}; clang_analyzer_eval(array[0] == 0x0F00); // expected-warning{{TRUE}} } + +// check that we propagate info through compound literal regions +void bar() { + int *integers = (int[]){1, 2, 3}; + clang_analyzer_eval(integers[0] == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(integers[1] == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(integers[2] == 3); // expected-warning{{TRUE}} + + int **pointers = (int *[]){[0], NULL}; + clang_analyzer_eval(pointers[0] == NULL); // expected-warning{{FALSE}} + clang_analyzer_eval(pointers[1] == NULL); // expected-warning{{TRUE}} +} diff --git a/clang/test/Analysis/retain-release-compound-literal.m b/clang/test/Analysis/retain-release-compound-literal.m new file mode 100644 index ..29a125346363 --- /dev/null +++ b/clang/test/Analysis/retain-release-compound-literal.m @@ -0,0 +1,25 @@ +// RUN: %clang_analyze_cc1 -verify -Wno-objc-root-class %s \ +// RUN: -analyzer-checker=core,osx.cocoa.RetainCount + +#define NULL 0 +#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained)) +#define CF_CONSUMED __attribute__((cf_consumed)) + +void clang_analyzer_eval(int); + +typedef const void *CFTypeRef; + +extern CFTypeRef CFCreate() CF_RETURNS_RETAINED; +extern CFTypeRef CFRetain(CFTypeRef cf); +extern void CFRelease(CFTypeRef cf); + +void bar(CFTypeRef *v) {} + +void test1() { + CFTypeRef *values = (CFTypeRef[]){ + CFCreate(), // no-warning + CFCreate(), // expected-warning{{leak}} + CFCreate()}; // no-warning + CFRelease(values[0]); + CFRelease(values[2]); +} diff --git a/clang/unittests/StaticAnalyzer/StoreTest.cpp b/clang/unittests/StaticAnalyzer/StoreTest.cpp index c8b930bf3247..17b64ce622f8 100644 --- a/clang/unittests/StaticAnalyzer/StoreTest.cpp +++ b/clang/unittests/StaticAnalyzer/StoreTest.cpp @@ -15,89 +15,139 @@ namespace clang { namespace ento { namespace { +class StoreTestConsumer : public ExprEngineConsumer { +public: + StoreTestConsumer(CompilerInstance ) : ExprEngineConsumer(C) {} + + bool HandleTopLevelDecl(DeclGroupRef DG) override { +for (const auto *D : DG) + performTest(D); +return true; + } + +private: + virtual void performTest(const Decl *D) = 0; +}; + +template class TestAction : public ASTFrontendAction { +public: + std::unique_ptr CreateASTConsumer(CompilerInstance , + StringRef File) override { +return std::make_unique(Compiler); + } +}; + // Test that we can put a value into an int-type variable and load it // back from that variable. Test what happens if default bindings are used. -class VariableBindConsumer : public ExprEngineConsumer { - void
[clang] a5b2503 - [analyzer] SATestBuild.py: Fix hang when one of the tasks fails
Author: Valeriy Savchenko Date: 2020-05-22T19:15:00+03:00 New Revision: a5b2503a8ab4fb21345fa9e2316530cdfaec1a60 URL: https://github.com/llvm/llvm-project/commit/a5b2503a8ab4fb21345fa9e2316530cdfaec1a60 DIFF: https://github.com/llvm/llvm-project/commit/a5b2503a8ab4fb21345fa9e2316530cdfaec1a60.diff LOG: [analyzer] SATestBuild.py: Fix hang when one of the tasks fails Summary: Tasks can crash with many different exceptions including SystemExit. Bare except still causes a warning, so let's use BaseException instead. Differential Revision: https://reviews.llvm.org/D80443 Added: Modified: clang/utils/analyzer/SATestBuild.py Removed: diff --git a/clang/utils/analyzer/SATestBuild.py b/clang/utils/analyzer/SATestBuild.py index 39fa7ece4362..e2fe6a95138b 100755 --- a/clang/utils/analyzer/SATestBuild.py +++ b/clang/utils/analyzer/SATestBuild.py @@ -633,7 +633,7 @@ def run(self): self.tasks_queue.task_done() -except CalledProcessError: +except BaseException: self.failure_flag.set() raise ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] 475d120 - [analyzer] SumTimerInfo.py: Partially modernize
Author: Valeriy Savchenko Date: 2020-05-22T13:51:58+03:00 New Revision: 475d1202837071959f3f69d3512c820538d81543 URL: https://github.com/llvm/llvm-project/commit/475d1202837071959f3f69d3512c820538d81543 DIFF: https://github.com/llvm/llvm-project/commit/475d1202837071959f3f69d3512c820538d81543.diff LOG: [analyzer] SumTimerInfo.py: Partially modernize Differential Revision: https://reviews.llvm.org/D80427 Added: Modified: clang/utils/analyzer/SumTimerInfo.py Removed: diff --git a/clang/utils/analyzer/SumTimerInfo.py b/clang/utils/analyzer/SumTimerInfo.py index 5d86f763f001..eed17e02e32e 100644 --- a/clang/utils/analyzer/SumTimerInfo.py +++ b/clang/utils/analyzer/SumTimerInfo.py @@ -6,8 +6,6 @@ Statistics are enabled by passing '-internal-stats' option to scan-build (or '-analyzer-stats' to the analyzer). """ -from __future__ import absolute_import, division, print_function - import sys if __name__ == '__main__': @@ -17,64 +15,65 @@ sys.exit(-1) f = open(sys.argv[1], 'r') -Time = 0.0 -TotalTime = 0.0 -MaxTime = 0.0 -Warnings = 0 -Count = 0 -FunctionsAnalyzed = 0 -ReachableBlocks = 0 -ReachedMaxSteps = 0 -NumSteps = 0 -NumInlinedCallSites = 0 -NumBifurcatedCallSites = 0 -MaxCFGSize = 0 +time = 0.0 +total_time = 0.0 +max_time = 0.0 +warnings = 0 +count = 0 +functions_analyzed = 0 +reachable_blocks = 0 +reached_max_steps = 0 +num_steps = 0 +num_inlined_call_sites = 0 +num_bifurcated_call_sites = 0 +max_cfg_size = 0 + for line in f: -if ("Analyzer Total Time" in line): +if "Analyzer total time" in line: s = line.split() -Time = Time + float(s[6]) -Count = Count + 1 -if (float(s[6]) > MaxTime): -MaxTime = float(s[6]) -if ("warning generated." in line) or ("warnings generated" in line): +time = time + float(s[6]) +count = count + 1 +if float(s[6]) > max_time: +max_time = float(s[6]) +if "warning generated." in line or "warnings generated" in line: s = line.split() -Warnings = Warnings + int(s[0]) +warnings = warnings + int(s[0]) if "The # of functions analysed (as top level)" in line: s = line.split() -FunctionsAnalyzed = FunctionsAnalyzed + int(s[0]) +functions_analyzed = functions_analyzed + int(s[0]) if "The % of reachable basic blocks" in line: s = line.split() -ReachableBlocks = ReachableBlocks + int(s[0]) +reachable_blocks = reachable_blocks + int(s[0]) if "The # of times we reached the max number of steps" in line: s = line.split() -ReachedMaxSteps = ReachedMaxSteps + int(s[0]) +reached_max_steps = reached_max_steps + int(s[0]) if "The maximum number of basic blocks in a function" in line: s = line.split() -if MaxCFGSize < int(s[0]): -MaxCFGSize = int(s[0]) +if max_cfg_size < int(s[0]): +max_cfg_size = int(s[0]) if "The # of steps executed" in line: s = line.split() -NumSteps = NumSteps + int(s[0]) +num_steps = num_steps + int(s[0]) if "The # of times we inlined a call" in line: s = line.split() -NumInlinedCallSites = NumInlinedCallSites + int(s[0]) +num_inlined_call_sites = num_inlined_call_sites + int(s[0]) if "The # of times we split the path due \ to imprecise dynamic dispatch info" in line: s = line.split() -NumBifurcatedCallSites = NumBifurcatedCallSites + int(s[0]) +num_bifurcated_call_sites = num_bifurcated_call_sites + int(s[0]) if ") Total" in line: s = line.split() -TotalTime = TotalTime + float(s[6]) +total_time = total_time + float(s[6]) -print("TU Count %d" % (Count)) -print("Time %f" % (Time)) -print("Warnings %d" % (Warnings)) -print("Functions Analyzed %d" % (FunctionsAnalyzed)) -print("Reachable Blocks %d" % (ReachableBlocks)) -print("Reached Max Steps %d" % (ReachedMaxSteps)) -print("Number of Steps %d" % (NumSteps)) -print("Number of Inlined calls %d (bifurcated %d)" % ( -NumInlinedCallSites, NumBifurcatedCallSites)) -print("MaxTime %f" % (MaxTime)) -print("TotalTime %f" % (TotalTime)) -print("Max CFG Size %d" % (MaxCFGSize)) +print(f"TU count {count}") +print(f"Time {time}") +print(f"Warnings {warnings}") +print(f"Functions analyzed {functions_analyzed}") +print(f"Reachable blocks {reachable_blocks}") +print(f"Reached max steps {reached_max_steps}") +print(f"Number of steps
[clang] 5a9aff1 - [analyzer] SATestUpdateDiffs.py: Refactor and add type annotations
Author: Valeriy Savchenko Date: 2020-05-22T13:51:58+03:00 New Revision: 5a9aff12ff3bc68109f41930ec296b7a19cbe76c URL: https://github.com/llvm/llvm-project/commit/5a9aff12ff3bc68109f41930ec296b7a19cbe76c DIFF: https://github.com/llvm/llvm-project/commit/5a9aff12ff3bc68109f41930ec296b7a19cbe76c.diff LOG: [analyzer] SATestUpdateDiffs.py: Refactor and add type annotations Differential Revision: https://reviews.llvm.org/D80426 Added: Modified: clang/utils/analyzer/SATestUpdateDiffs.py Removed: diff --git a/clang/utils/analyzer/SATestUpdateDiffs.py b/clang/utils/analyzer/SATestUpdateDiffs.py index ac2832d40e6e..e89b06dd75eb 100755 --- a/clang/utils/analyzer/SATestUpdateDiffs.py +++ b/clang/utils/analyzer/SATestUpdateDiffs.py @@ -3,67 +3,67 @@ """ Update reference results for static analyzer. """ -from __future__ import absolute_import, division, print_function - import SATestBuild -from subprocess import check_call import os +import shutil import sys +from subprocess import check_call + Verbose = 0 -def runCmd(Command, **kwargs): -if Verbose: -print("Executing %s" % Command) -check_call(Command, shell=True, **kwargs) +def update_reference_results(project_name: str, build_mode: int): +project_info = SATestBuild.ProjectInfo(project_name, build_mode) +tester = SATestBuild.ProjectTester(project_info) +project_dir = tester.get_project_dir() +tester.is_reference_build = True +ref_results_path = os.path.join(project_dir, tester.get_output_dir()) -def updateReferenceResults(ProjName, ProjBuildMode): -ProjInfo = SATestBuild.ProjectInfo(ProjName, ProjBuildMode) -ProjTester = SATestBuild.ProjectTester(ProjInfo) -ProjDir = ProjTester.get_project_dir() +tester.is_reference_build = False +created_results_path = os.path.join(project_dir, tester.get_output_dir()) -ProjTester.is_reference_build = True -RefResultsPath = os.path.join(ProjDir, ProjTester.get_output_dir()) +if not os.path.exists(created_results_path): +print("New results not found, was SATestBuild.py previously run?", + file=sys.stderr) +sys.exit(1) -ProjTester.is_reference_build = False -CreatedResultsPath = os.path.join(ProjDir, ProjTester.get_output_dir()) +build_log_path = SATestBuild.get_build_log_path(ref_results_path) +build_log_dir = os.path.dirname(os.path.abspath(build_log_path)) -if not os.path.exists(CreatedResultsPath): -print("New results not found, was SATestBuild.py " - "previously run?", file=sys.stderr) -sys.exit(1) +os.makedirs(build_log_dir) + +with open(build_log_path, "w+") as build_log_file: +def run_cmd(command: str): +if Verbose: +print(f"Executing {command}") +check_call(command, shell=True, stdout=build_log_file) -BuildLogPath = SATestBuild.get_build_log_path(RefResultsPath) -Dirname = os.path.dirname(os.path.abspath(BuildLogPath)) -runCmd("mkdir -p '%s'" % Dirname) -with open(BuildLogPath, "w+") as PBuildLogFile: # Remove reference results: in git, and then again for a good measure # with rm, as git might not remove things fully if there are empty # directories involved. -runCmd('git rm -r -q "%s"' % (RefResultsPath,), stdout=PBuildLogFile) -runCmd('rm -rf "%s"' % (RefResultsPath,), stdout=PBuildLogFile) +run_cmd(f"git rm -r -q '{ref_results_path}'") +shutil.rmtree(ref_results_path) # Replace reference results with a freshly computed once. -runCmd('cp -r "%s" "%s"' % (CreatedResultsPath, RefResultsPath,), - stdout=PBuildLogFile) +shutil.copytree(created_results_path, ref_results_path, symlinks=True) # Run cleanup script. -SATestBuild.run_cleanup_script(ProjDir, PBuildLogFile) +SATestBuild.run_cleanup_script(project_dir, build_log_file) SATestBuild.normalize_reference_results( -ProjDir, RefResultsPath, ProjBuildMode) +project_dir, ref_results_path, build_mode) # Clean up the generated diff erence results. -SATestBuild.cleanup_reference_results(RefResultsPath) +SATestBuild.cleanup_reference_results(ref_results_path) -runCmd('git add "%s"' % (RefResultsPath,), stdout=PBuildLogFile) +run_cmd(f"git add '{ref_results_path}'") def main(argv): -if len(argv) == 2 and argv[1] in ('-h', '--help'): +if len(argv) == 2 and argv[1] in ("-h", "--help"): print("Update static analyzer reference results based " "\non the previous run of SATestBuild.py.\n" "\nN.B.: Assumes that SATestBuild.py was just run", @@ -71,8 +71,8 @@ def main(argv): sys.exit(1) with open(SATestBuild.get_project_map_path(), "r") as f: -for ProjName,