summaryrefslogtreecommitdiff
path: root/clang/lib/Sema/AnalysisBasedWarnings.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Sema/AnalysisBasedWarnings.cpp')
-rw-r--r--clang/lib/Sema/AnalysisBasedWarnings.cpp276
1 files changed, 230 insertions, 46 deletions
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index ec8acbdff3b4..d1400cbfc884 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -37,6 +37,7 @@
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CFGStmtMap.h"
+#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/SourceLocation.h"
@@ -46,6 +47,7 @@
#include "clang/Sema/SemaInternal.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
@@ -401,6 +403,142 @@ static bool isNoexcept(const FunctionDecl *FD) {
return false;
}
+/// Checks if the given expression is a reference to a function with
+/// 'noreturn' attribute.
+static bool isReferenceToNoReturn(const Expr *E) {
+ if (auto *DRef = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
+ if (auto *FD = dyn_cast<FunctionDecl>(DRef->getDecl()))
+ return FD->isNoReturn();
+ return false;
+}
+
+/// Checks if the given variable, which is assumed to be a function pointer, is
+/// initialized with a function having 'noreturn' attribute.
+static bool isInitializedWithNoReturn(const VarDecl *VD) {
+ if (const Expr *Init = VD->getInit()) {
+ if (auto *ListInit = dyn_cast<InitListExpr>(Init);
+ ListInit && ListInit->getNumInits() > 0)
+ Init = ListInit->getInit(0);
+ return isReferenceToNoReturn(Init);
+ }
+ return false;
+}
+
+namespace {
+
+/// Looks for statements, that can define value of the given variable.
+struct TransferFunctions : public StmtVisitor<TransferFunctions> {
+ const VarDecl *Var;
+ std::optional<bool> AllValuesAreNoReturn;
+
+ TransferFunctions(const VarDecl *VD) : Var(VD) {}
+
+ void reset() { AllValuesAreNoReturn = std::nullopt; }
+
+ void VisitDeclStmt(DeclStmt *DS) {
+ for (auto *DI : DS->decls())
+ if (auto *VD = dyn_cast<VarDecl>(DI))
+ if (VarDecl *Def = VD->getDefinition())
+ if (Def == Var)
+ AllValuesAreNoReturn = isInitializedWithNoReturn(Def);
+ }
+
+ void VisitUnaryOperator(UnaryOperator *UO) {
+ if (UO->getOpcode() == UO_AddrOf) {
+ if (auto *DRef =
+ dyn_cast<DeclRefExpr>(UO->getSubExpr()->IgnoreParenCasts()))
+ if (DRef->getDecl() == Var)
+ AllValuesAreNoReturn = false;
+ }
+ }
+
+ void VisitBinaryOperator(BinaryOperator *BO) {
+ if (BO->getOpcode() == BO_Assign)
+ if (auto *DRef = dyn_cast<DeclRefExpr>(BO->getLHS()->IgnoreParenCasts()))
+ if (DRef->getDecl() == Var)
+ AllValuesAreNoReturn = isReferenceToNoReturn(BO->getRHS());
+ }
+
+ void VisitCallExpr(CallExpr *CE) {
+ for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end(); I != E;
+ ++I) {
+ const Expr *Arg = *I;
+ if (Arg->isGLValue() && !Arg->getType().isConstQualified())
+ if (auto *DRef = dyn_cast<DeclRefExpr>(Arg->IgnoreParenCasts()))
+ if (auto VD = dyn_cast<VarDecl>(DRef->getDecl()))
+ if (VD->getDefinition() == Var)
+ AllValuesAreNoReturn = false;
+ }
+ }
+};
+} // namespace
+
+// Checks if all possible values of the given variable are functions with
+// 'noreturn' attribute.
+static bool areAllValuesNoReturn(const VarDecl *VD, const CFGBlock &VarBlk,
+ AnalysisDeclContext &AC) {
+ // The set of possible values of a constant variable is determined by
+ // its initializer, unless it is a function parameter.
+ if (!isa<ParmVarDecl>(VD) && VD->getType().isConstant(AC.getASTContext())) {
+ if (const VarDecl *Def = VD->getDefinition())
+ return isInitializedWithNoReturn(Def);
+ return false;
+ }
+
+ // In multithreaded environment the value of a global variable may be changed
+ // asynchronously.
+ if (!VD->getDeclContext()->isFunctionOrMethod())
+ return false;
+
+ // Check the condition "all values are noreturn". It is satisfied if the
+ // variable is set to "noreturn" value in the current block or all its
+ // predecessors satisfies the condition.
+ using MapTy = llvm::DenseMap<const CFGBlock *, std::optional<bool>>;
+ using ValueTy = MapTy::value_type;
+ MapTy BlocksToCheck;
+ BlocksToCheck[&VarBlk] = std::nullopt;
+ const auto BlockSatisfiesCondition = [](ValueTy Item) {
+ return Item.getSecond().value_or(false);
+ };
+
+ TransferFunctions TF(VD);
+ BackwardDataflowWorklist Worklist(*AC.getCFG(), AC);
+ Worklist.enqueueBlock(&VarBlk);
+ while (const CFGBlock *B = Worklist.dequeue()) {
+ // First check the current block.
+ for (CFGBlock::const_reverse_iterator ri = B->rbegin(), re = B->rend();
+ ri != re; ++ri) {
+ if (std::optional<CFGStmt> cs = ri->getAs<CFGStmt>()) {
+ const Stmt *S = cs->getStmt();
+ TF.reset();
+ TF.Visit(const_cast<Stmt *>(S));
+ if (TF.AllValuesAreNoReturn) {
+ if (!TF.AllValuesAreNoReturn.value())
+ return false;
+ BlocksToCheck[B] = true;
+ break;
+ }
+ }
+ }
+
+ // If all checked blocks satisfy the condition, the check is finished.
+ if (llvm::all_of(BlocksToCheck, BlockSatisfiesCondition))
+ return true;
+
+ // If this block does not contain the variable definition, check
+ // its predecessors.
+ if (!BlocksToCheck[B]) {
+ Worklist.enqueuePredecessors(B);
+ BlocksToCheck.erase(B);
+ for (const auto &PredBlk : B->preds())
+ if (!BlocksToCheck.contains(PredBlk))
+ BlocksToCheck[PredBlk] = std::nullopt;
+ }
+ }
+
+ return false;
+}
+
//===----------------------------------------------------------------------===//
// Check for missing return value.
//===----------------------------------------------------------------------===//
@@ -527,6 +665,17 @@ static ControlFlowKind CheckFallThrough(AnalysisDeclContext &AC) {
HasAbnormalEdge = true;
continue;
}
+ if (auto *Call = dyn_cast<CallExpr>(S)) {
+ const Expr *Callee = Call->getCallee();
+ if (Callee->getType()->isPointerType())
+ if (auto *DeclRef =
+ dyn_cast<DeclRefExpr>(Callee->IgnoreParenImpCasts()))
+ if (auto *VD = dyn_cast<VarDecl>(DeclRef->getDecl()))
+ if (areAllValuesNoReturn(VD, B, AC)) {
+ HasAbnormalEdge = true;
+ continue;
+ }
+ }
HasPlainEdge = true;
}
@@ -987,10 +1136,19 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use,
}
/// Diagnose uninitialized const reference usages.
-static void DiagnoseUninitializedConstRefUse(Sema &S, const VarDecl *VD,
+static bool DiagnoseUninitializedConstRefUse(Sema &S, const VarDecl *VD,
const UninitUse &Use) {
S.Diag(Use.getUser()->getBeginLoc(), diag::warn_uninit_const_reference)
<< VD->getDeclName() << Use.getUser()->getSourceRange();
+ return !S.getDiagnostics().isLastDiagnosticIgnored();
+}
+
+/// Diagnose uninitialized const pointer usages.
+static bool DiagnoseUninitializedConstPtrUse(Sema &S, const VarDecl *VD,
+ const UninitUse &Use) {
+ S.Diag(Use.getUser()->getBeginLoc(), diag::warn_uninit_const_pointer)
+ << VD->getDeclName() << Use.getUser()->getSourceRange();
+ return !S.getDiagnostics().isLastDiagnosticIgnored();
}
/// DiagnoseUninitializedUse -- Helper function for diagnosing uses of an
@@ -1022,7 +1180,7 @@ static bool DiagnoseUninitializedUse(Sema &S, const VarDecl *VD,
if (CR.doesContainReference()) {
S.Diag(DRE->getBeginLoc(), diag::warn_uninit_self_reference_in_init)
<< VD->getDeclName() << VD->getLocation() << DRE->getSourceRange();
- return true;
+ return !S.getDiagnostics().isLastDiagnosticIgnored();
}
}
@@ -1045,7 +1203,7 @@ static bool DiagnoseUninitializedUse(Sema &S, const VarDecl *VD,
S.Diag(VD->getBeginLoc(), diag::note_var_declared_here)
<< VD->getDeclName();
- return true;
+ return !S.getDiagnostics().isLastDiagnosticIgnored();
}
namespace {
@@ -1559,43 +1717,7 @@ public:
UsesVec *vec = V.getPointer();
bool hasSelfInit = V.getInt();
- // Specially handle the case where we have uses of an uninitialized
- // variable, but the root cause is an idiomatic self-init. We want
- // to report the diagnostic at the self-init since that is the root cause.
- if (!vec->empty() && hasSelfInit && hasAlwaysUninitializedUse(vec))
- DiagnoseUninitializedUse(S, vd,
- UninitUse(vd->getInit()->IgnoreParenCasts(),
- /* isAlwaysUninit */ true),
- /* alwaysReportSelfInit */ true);
- else {
- // Sort the uses by their SourceLocations. While not strictly
- // guaranteed to produce them in line/column order, this will provide
- // a stable ordering.
- llvm::sort(*vec, [](const UninitUse &a, const UninitUse &b) {
- // Move ConstRef uses to the back.
- if (a.isConstRefUse() != b.isConstRefUse())
- return b.isConstRefUse();
- // Prefer a more confident report over a less confident one.
- if (a.getKind() != b.getKind())
- return a.getKind() > b.getKind();
- return a.getUser()->getBeginLoc() < b.getUser()->getBeginLoc();
- });
-
- for (const auto &U : *vec) {
- if (U.isConstRefUse()) {
- DiagnoseUninitializedConstRefUse(S, vd, U);
- break;
- }
-
- // If we have self-init, downgrade all uses to 'may be uninitialized'.
- UninitUse Use = hasSelfInit ? UninitUse(U.getUser(), false) : U;
-
- if (DiagnoseUninitializedUse(S, vd, Use))
- // Skip further diagnostics for this variable. We try to warn only
- // on the first point at which a variable is used uninitialized.
- break;
- }
- }
+ diagnoseUnitializedVar(vd, hasSelfInit, vec);
// Release the uses vector.
delete vec;
@@ -1612,6 +1734,52 @@ private:
U.getKind() == UninitUse::AfterDecl;
});
}
+
+ // Print the diagnostic for the variable. We try to warn only on the first
+ // point at which a variable is used uninitialized. After the first
+ // diagnostic is printed, further diagnostics for this variable are skipped.
+ void diagnoseUnitializedVar(const VarDecl *vd, bool hasSelfInit,
+ UsesVec *vec) {
+ // Specially handle the case where we have uses of an uninitialized
+ // variable, but the root cause is an idiomatic self-init. We want
+ // to report the diagnostic at the self-init since that is the root cause.
+ if (hasSelfInit && hasAlwaysUninitializedUse(vec)) {
+ if (DiagnoseUninitializedUse(S, vd,
+ UninitUse(vd->getInit()->IgnoreParenCasts(),
+ /*isAlwaysUninit=*/true),
+ /*alwaysReportSelfInit=*/true))
+ return;
+ }
+
+ // Sort the uses by their SourceLocations. While not strictly
+ // guaranteed to produce them in line/column order, this will provide
+ // a stable ordering.
+ llvm::sort(*vec, [](const UninitUse &a, const UninitUse &b) {
+ // Prefer the direct use of an uninitialized variable over its use via
+ // constant reference or pointer.
+ if (a.isConstRefOrPtrUse() != b.isConstRefOrPtrUse())
+ return b.isConstRefOrPtrUse();
+ // Prefer a more confident report over a less confident one.
+ if (a.getKind() != b.getKind())
+ return a.getKind() > b.getKind();
+ return a.getUser()->getBeginLoc() < b.getUser()->getBeginLoc();
+ });
+
+ for (const auto &U : *vec) {
+ if (U.isConstRefUse()) {
+ if (DiagnoseUninitializedConstRefUse(S, vd, U))
+ return;
+ } else if (U.isConstPtrUse()) {
+ if (DiagnoseUninitializedConstPtrUse(S, vd, U))
+ return;
+ } else {
+ // If we have self-init, downgrade all uses to 'may be uninitialized'.
+ UninitUse Use = hasSelfInit ? UninitUse(U.getUser(), false) : U;
+ if (DiagnoseUninitializedUse(S, vd, Use))
+ return;
+ }
+ }
+ }
};
/// Inter-procedural data for the called-once checker.
@@ -1943,11 +2111,26 @@ class ThreadSafetyReporter : public clang::threadSafety::ThreadSafetyHandler {
void handleNoMutexHeld(const NamedDecl *D, ProtectedOperationKind POK,
AccessKind AK, SourceLocation Loc) override {
- assert((POK == POK_VarAccess || POK == POK_VarDereference) &&
- "Only works for variables");
- unsigned DiagID = POK == POK_VarAccess?
- diag::warn_variable_requires_any_lock:
- diag::warn_var_deref_requires_any_lock;
+ unsigned DiagID = 0;
+ switch (POK) {
+ case POK_VarAccess:
+ case POK_PassByRef:
+ case POK_ReturnByRef:
+ case POK_PassPointer:
+ case POK_ReturnPointer:
+ DiagID = diag::warn_variable_requires_any_lock;
+ break;
+ case POK_VarDereference:
+ case POK_PtPassByRef:
+ case POK_PtReturnByRef:
+ case POK_PtPassPointer:
+ case POK_PtReturnPointer:
+ DiagID = diag::warn_var_deref_requires_any_lock;
+ break;
+ case POK_FunctionCall:
+ llvm_unreachable("Only works for variables");
+ break;
+ }
PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID)
<< D << getLockKindFromAccessKind(AK));
Warnings.emplace_back(std::move(Warning), getNotes());
@@ -2820,7 +3003,8 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
if (!Diags.isIgnored(diag::warn_uninit_var, D->getBeginLoc()) ||
!Diags.isIgnored(diag::warn_sometimes_uninit_var, D->getBeginLoc()) ||
!Diags.isIgnored(diag::warn_maybe_uninit_var, D->getBeginLoc()) ||
- !Diags.isIgnored(diag::warn_uninit_const_reference, D->getBeginLoc())) {
+ !Diags.isIgnored(diag::warn_uninit_const_reference, D->getBeginLoc()) ||
+ !Diags.isIgnored(diag::warn_uninit_const_pointer, D->getBeginLoc())) {
if (CFG *cfg = AC.getCFG()) {
UninitValsDiagReporter reporter(S);
UninitVariablesAnalysisStats stats;