|
@@ -22,6 +22,7 @@
|
|
|
#include "clang/AST/ExprObjC.h"
|
|
|
#include "clang/AST/Stmt.h"
|
|
|
#include "clang/AST/Type.h"
|
|
|
+#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
|
#include "clang/Analysis/AnalysisDeclContext.h"
|
|
|
#include "clang/Analysis/CFG.h"
|
|
|
#include "clang/Analysis/CFGStmtMap.h"
|
|
@@ -306,15 +307,26 @@ public:
|
|
|
|
|
|
CallEventRef<> Call =
|
|
|
BRC.getStateManager().getCallEventManager().getCaller(SCtx, State);
|
|
|
-
|
|
|
const PrintingPolicy &PP = BRC.getASTContext().getPrintingPolicy();
|
|
|
const SourceManager &SM = BRC.getSourceManager();
|
|
|
+
|
|
|
+ // Region of interest corresponds to an IVar, exiting a method
|
|
|
+ // which could have written into that IVar, but did not.
|
|
|
+ if (const auto *MC = dyn_cast<ObjCMethodCall>(Call))
|
|
|
+ if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest))
|
|
|
+ if (potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(),
|
|
|
+ IvarR->getDecl()) &&
|
|
|
+ !isRegionOfInterestModifiedInFrame(N))
|
|
|
+ return notModifiedMemberDiagnostics(
|
|
|
+ Ctx, SM, PP, *CallExitLoc, Call,
|
|
|
+ MC->getReceiverSVal().getAsRegion());
|
|
|
+
|
|
|
if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) {
|
|
|
const MemRegion *ThisRegion = CCall->getCXXThisVal().getAsRegion();
|
|
|
if (RegionOfInterest->isSubRegionOf(ThisRegion)
|
|
|
&& !CCall->getDecl()->isImplicit()
|
|
|
&& !isRegionOfInterestModifiedInFrame(N))
|
|
|
- return notModifiedInConstructorDiagnostics(Ctx, SM, PP, *CallExitLoc,
|
|
|
+ return notModifiedMemberDiagnostics(Ctx, SM, PP, *CallExitLoc,
|
|
|
CCall, ThisRegion);
|
|
|
}
|
|
|
|
|
@@ -331,7 +343,7 @@ public:
|
|
|
if (isRegionOfInterestModifiedInFrame(N))
|
|
|
return nullptr;
|
|
|
|
|
|
- return notModifiedDiagnostics(
|
|
|
+ return notModifiedParameterDiagnostics(
|
|
|
Ctx, SM, PP, *CallExitLoc, Call, PVD, R, IndirectionLevel);
|
|
|
}
|
|
|
QualType PT = T->getPointeeType();
|
|
@@ -346,6 +358,22 @@ public:
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
+
|
|
|
+ /// \return Whether the method declaration \p Parent
|
|
|
+ /// syntactically has a binary operation writing into the ivar \p Ivar.
|
|
|
+ bool potentiallyWritesIntoIvar(const Decl *Parent,
|
|
|
+ const ObjCIvarDecl *Ivar) {
|
|
|
+ using namespace ast_matchers;
|
|
|
+ if (!Parent || !Parent->getBody())
|
|
|
+ return false;
|
|
|
+ StatementMatcher WriteIntoIvarM = binaryOperator(
|
|
|
+ hasOperatorName("="), hasLHS(ignoringParenImpCasts(objcIvarRefExpr(
|
|
|
+ hasDeclaration(equalsNode(Ivar))))));
|
|
|
+ StatementMatcher ParentM = stmt(hasDescendant(WriteIntoIvarM));
|
|
|
+ auto Matches = match(ParentM, *Parent->getBody(), Parent->getASTContext());
|
|
|
+ return !Matches.empty();
|
|
|
+ }
|
|
|
+
|
|
|
/// Check and lazily calculate whether the region of interest is
|
|
|
/// modified in the stack frame to which \p N belongs.
|
|
|
/// The calculation is cached in FramesModifyingRegion.
|
|
@@ -414,19 +442,21 @@ private:
|
|
|
Ty->getPointeeType().getCanonicalType().isConstQualified();
|
|
|
}
|
|
|
|
|
|
- std::shared_ptr<PathDiagnosticPiece> notModifiedInConstructorDiagnostics(
|
|
|
+ /// \return Diagnostics piece for the member field not modified
|
|
|
+ /// in a given function.
|
|
|
+ std::shared_ptr<PathDiagnosticPiece> notModifiedMemberDiagnostics(
|
|
|
const LocationContext *Ctx,
|
|
|
const SourceManager &SM,
|
|
|
const PrintingPolicy &PP,
|
|
|
CallExitBegin &CallExitLoc,
|
|
|
- const CXXConstructorCall *Call,
|
|
|
+ CallEventRef<> Call,
|
|
|
const MemRegion *ArgRegion) {
|
|
|
+ const char *TopRegionName = isa<ObjCMethodCall>(Call) ? "self" : "this";
|
|
|
SmallString<256> sbuf;
|
|
|
llvm::raw_svector_ostream os(sbuf);
|
|
|
os << DiagnosticsMsg;
|
|
|
- bool out = prettyPrintRegionName(
|
|
|
- "this", "->", /*IsReference=*/true,
|
|
|
- /*IndirectionLevel=*/1, ArgRegion, os, PP);
|
|
|
+ bool out = prettyPrintRegionName(TopRegionName, "->", /*IsReference=*/true,
|
|
|
+ /*IndirectionLevel=*/1, ArgRegion, os, PP);
|
|
|
|
|
|
// Return nothing if we have failed to pretty-print.
|
|
|
if (!out)
|
|
@@ -434,14 +464,16 @@ private:
|
|
|
|
|
|
os << "'";
|
|
|
PathDiagnosticLocation L =
|
|
|
- getPathDiagnosticLocation(nullptr, SM, Ctx, Call);
|
|
|
+ getPathDiagnosticLocation(CallExitLoc.getReturnStmt(), SM, Ctx, Call);
|
|
|
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
|
|
|
}
|
|
|
|
|
|
+ /// \return Diagnostics piece for the parameter \p PVD not modified
|
|
|
+ /// in a given function.
|
|
|
/// \p IndirectionLevel How many times \c ArgRegion has to be dereferenced
|
|
|
/// before we get to the super region of \c RegionOfInterest
|
|
|
std::shared_ptr<PathDiagnosticPiece>
|
|
|
- notModifiedDiagnostics(const LocationContext *Ctx,
|
|
|
+ notModifiedParameterDiagnostics(const LocationContext *Ctx,
|
|
|
const SourceManager &SM,
|
|
|
const PrintingPolicy &PP,
|
|
|
CallExitBegin &CallExitLoc,
|
|
@@ -481,8 +513,8 @@ private:
|
|
|
|
|
|
/// Pretty-print region \p ArgRegion starting from parent to \p os.
|
|
|
/// \return whether printing has succeeded
|
|
|
- bool prettyPrintRegionName(const char *TopRegionName,
|
|
|
- const char *Sep,
|
|
|
+ bool prettyPrintRegionName(StringRef TopRegionName,
|
|
|
+ StringRef Sep,
|
|
|
bool IsReference,
|
|
|
int IndirectionLevel,
|
|
|
const MemRegion *ArgRegion,
|
|
@@ -491,7 +523,8 @@ private:
|
|
|
SmallVector<const MemRegion *, 5> Subregions;
|
|
|
const MemRegion *R = RegionOfInterest;
|
|
|
while (R != ArgRegion) {
|
|
|
- if (!(isa<FieldRegion>(R) || isa<CXXBaseObjectRegion>(R)))
|
|
|
+ if (!(isa<FieldRegion>(R) || isa<CXXBaseObjectRegion>(R) ||
|
|
|
+ isa<ObjCIvarRegion>(R)))
|
|
|
return false; // Pattern-matching failed.
|
|
|
Subregions.push_back(R);
|
|
|
R = cast<SubRegion>(R)->getSuperRegion();
|
|
@@ -519,6 +552,10 @@ private:
|
|
|
os << Sep;
|
|
|
FR->getDecl()->getDeclName().print(os, PP);
|
|
|
Sep = ".";
|
|
|
+ } else if (const auto *IR = dyn_cast<ObjCIvarRegion>(*I)) {
|
|
|
+ os << "->";
|
|
|
+ IR->getDecl()->getDeclName().print(os, PP);
|
|
|
+ Sep = ".";
|
|
|
} else if (isa<CXXBaseObjectRegion>(*I)) {
|
|
|
continue; // Just keep going up to the base region.
|
|
|
} else {
|