2010-03-21 05:06:02 +08:00
|
|
|
//=- AnalysisBasedWarnings.cpp - Sema warnings based on libAnalysis -*- C++ -*-=//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file defines analysis_warnings::[Policy,Executor].
|
|
|
|
// Together they are used by Sema to issue warnings based on inexpensive
|
|
|
|
// static analysis algorithms in libAnalysis.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2010-08-13 04:07:10 +08:00
|
|
|
#include "clang/Sema/AnalysisBasedWarnings.h"
|
2010-08-26 06:03:47 +08:00
|
|
|
#include "clang/Sema/SemaInternal.h"
|
2011-02-23 09:52:04 +08:00
|
|
|
#include "clang/Sema/ScopeInfo.h"
|
2010-03-21 05:11:09 +08:00
|
|
|
#include "clang/Basic/SourceManager.h"
|
2011-09-10 00:04:02 +08:00
|
|
|
#include "clang/Basic/SourceLocation.h"
|
2011-01-22 03:41:46 +08:00
|
|
|
#include "clang/Lex/Preprocessor.h"
|
2010-08-24 15:21:54 +08:00
|
|
|
#include "clang/AST/DeclObjC.h"
|
2010-08-25 15:42:41 +08:00
|
|
|
#include "clang/AST/DeclCXX.h"
|
2010-03-21 05:06:02 +08:00
|
|
|
#include "clang/AST/ExprObjC.h"
|
|
|
|
#include "clang/AST/ExprCXX.h"
|
|
|
|
#include "clang/AST/StmtObjC.h"
|
|
|
|
#include "clang/AST/StmtCXX.h"
|
2011-04-05 04:56:00 +08:00
|
|
|
#include "clang/AST/EvaluatedExprVisitor.h"
|
2011-08-24 02:46:34 +08:00
|
|
|
#include "clang/AST/StmtVisitor.h"
|
2010-03-21 05:06:02 +08:00
|
|
|
#include "clang/Analysis/AnalysisContext.h"
|
|
|
|
#include "clang/Analysis/CFG.h"
|
|
|
|
#include "clang/Analysis/Analyses/ReachableCode.h"
|
2011-02-23 09:52:04 +08:00
|
|
|
#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
|
2011-09-10 00:11:56 +08:00
|
|
|
#include "clang/Analysis/Analyses/ThreadSafety.h"
|
2011-02-23 09:52:04 +08:00
|
|
|
#include "clang/Analysis/CFGStmtMap.h"
|
2011-03-15 11:17:07 +08:00
|
|
|
#include "clang/Analysis/Analyses/UninitializedValues.h"
|
2010-03-21 05:06:02 +08:00
|
|
|
#include "llvm/ADT/BitVector.h"
|
2011-08-24 02:46:34 +08:00
|
|
|
#include "llvm/ADT/FoldingSet.h"
|
|
|
|
#include "llvm/ADT/ImmutableMap.h"
|
|
|
|
#include "llvm/ADT/PostOrderIterator.h"
|
|
|
|
#include "llvm/ADT/SmallVector.h"
|
2011-09-10 00:04:02 +08:00
|
|
|
#include "llvm/ADT/StringRef.h"
|
2010-03-21 05:06:02 +08:00
|
|
|
#include "llvm/Support/Casting.h"
|
2011-08-24 02:46:34 +08:00
|
|
|
#include <algorithm>
|
|
|
|
#include <vector>
|
2010-03-21 05:06:02 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Unreachable code analysis.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
class UnreachableCodeHandler : public reachable_code::Callback {
|
|
|
|
Sema &S;
|
|
|
|
public:
|
|
|
|
UnreachableCodeHandler(Sema &s) : S(s) {}
|
|
|
|
|
|
|
|
void HandleUnreachable(SourceLocation L, SourceRange R1, SourceRange R2) {
|
|
|
|
S.Diag(L, diag::warn_unreachable) << R1 << R2;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/// CheckUnreachable - Check for unreachable code.
|
2011-10-24 09:32:45 +08:00
|
|
|
static void CheckUnreachable(Sema &S, AnalysisDeclContext &AC) {
|
2010-03-21 05:06:02 +08:00
|
|
|
UnreachableCodeHandler UC(S);
|
|
|
|
reachable_code::FindUnreachableCode(AC, UC);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Check for missing return value.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2010-05-16 17:34:11 +08:00
|
|
|
enum ControlFlowKind {
|
|
|
|
UnknownFallThrough,
|
|
|
|
NeverFallThrough,
|
|
|
|
MaybeFallThrough,
|
|
|
|
AlwaysFallThrough,
|
|
|
|
NeverFallThroughOrReturn
|
|
|
|
};
|
2010-03-21 05:06:02 +08:00
|
|
|
|
|
|
|
/// CheckFallThrough - Check that we don't fall off the end of a
|
|
|
|
/// Statement that should return a value.
|
|
|
|
///
|
|
|
|
/// \returns AlwaysFallThrough iff we always fall off the end of the statement,
|
|
|
|
/// MaybeFallThrough iff we might or might not fall off the end,
|
|
|
|
/// NeverFallThroughOrReturn iff we never fall off the end of the statement or
|
|
|
|
/// return. We assume NeverFallThrough iff we never fall off the end of the
|
|
|
|
/// statement but we may return. We assume that functions not marked noreturn
|
|
|
|
/// will return.
|
2011-10-24 09:32:45 +08:00
|
|
|
static ControlFlowKind CheckFallThrough(AnalysisDeclContext &AC) {
|
2010-03-21 05:06:02 +08:00
|
|
|
CFG *cfg = AC.getCFG();
|
2010-05-16 17:34:11 +08:00
|
|
|
if (cfg == 0) return UnknownFallThrough;
|
2010-03-21 05:06:02 +08:00
|
|
|
|
|
|
|
// The CFG leaves in dead things, and we don't want the dead code paths to
|
|
|
|
// confuse us, so we mark all live things first.
|
|
|
|
llvm::BitVector live(cfg->getNumBlockIDs());
|
2011-08-24 07:05:11 +08:00
|
|
|
unsigned count = reachable_code::ScanReachableFromBlock(&cfg->getEntry(),
|
2010-03-21 05:06:02 +08:00
|
|
|
live);
|
|
|
|
|
|
|
|
bool AddEHEdges = AC.getAddEHEdges();
|
|
|
|
if (!AddEHEdges && count != cfg->getNumBlockIDs())
|
|
|
|
// When there are things remaining dead, and we didn't add EH edges
|
|
|
|
// from CallExprs to the catch clauses, we have to go back and
|
|
|
|
// mark them as live.
|
|
|
|
for (CFG::iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) {
|
|
|
|
CFGBlock &b = **I;
|
|
|
|
if (!live[b.getBlockID()]) {
|
|
|
|
if (b.pred_begin() == b.pred_end()) {
|
|
|
|
if (b.getTerminator() && isa<CXXTryStmt>(b.getTerminator()))
|
|
|
|
// When not adding EH edges from calls, catch clauses
|
|
|
|
// can otherwise seem dead. Avoid noting them as dead.
|
2011-08-24 07:05:11 +08:00
|
|
|
count += reachable_code::ScanReachableFromBlock(&b, live);
|
2010-03-21 05:06:02 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now we know what is live, we check the live precessors of the exit block
|
|
|
|
// and look for fall through paths, being careful to ignore normal returns,
|
|
|
|
// and exceptional paths.
|
|
|
|
bool HasLiveReturn = false;
|
|
|
|
bool HasFakeEdge = false;
|
|
|
|
bool HasPlainEdge = false;
|
|
|
|
bool HasAbnormalEdge = false;
|
2010-09-09 08:06:07 +08:00
|
|
|
|
|
|
|
// Ignore default cases that aren't likely to be reachable because all
|
|
|
|
// enums in a switch(X) have explicit case statements.
|
|
|
|
CFGBlock::FilterOptions FO;
|
|
|
|
FO.IgnoreDefaultsWithCoveredEnums = 1;
|
|
|
|
|
|
|
|
for (CFGBlock::filtered_pred_iterator
|
|
|
|
I = cfg->getExit().filtered_pred_start_end(FO); I.hasMore(); ++I) {
|
|
|
|
const CFGBlock& B = **I;
|
2010-03-21 05:06:02 +08:00
|
|
|
if (!live[B.getBlockID()])
|
|
|
|
continue;
|
2011-01-26 12:49:52 +08:00
|
|
|
|
2011-09-13 17:53:58 +08:00
|
|
|
// Skip blocks which contain an element marked as no-return. They don't
|
|
|
|
// represent actually viable edges into the exit block, so mark them as
|
|
|
|
// abnormal.
|
|
|
|
if (B.hasNoReturnElement()) {
|
|
|
|
HasAbnormalEdge = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-01-26 12:49:52 +08:00
|
|
|
// Destructors can appear after the 'return' in the CFG. This is
|
|
|
|
// normal. We need to look pass the destructors for the return
|
|
|
|
// statement (if it exists).
|
|
|
|
CFGBlock::const_reverse_iterator ri = B.rbegin(), re = B.rend();
|
2011-09-13 17:53:58 +08:00
|
|
|
|
|
|
|
for ( ; ri != re ; ++ri)
|
|
|
|
if (isa<CFGStmt>(*ri))
|
2011-01-26 12:49:52 +08:00
|
|
|
break;
|
2011-09-13 17:53:58 +08:00
|
|
|
|
2011-01-26 12:49:52 +08:00
|
|
|
// No more CFGElements in the block?
|
|
|
|
if (ri == re) {
|
2010-03-21 05:06:02 +08:00
|
|
|
if (B.getTerminator() && isa<CXXTryStmt>(B.getTerminator())) {
|
|
|
|
HasAbnormalEdge = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// A labeled empty statement, or the entry block...
|
|
|
|
HasPlainEdge = true;
|
|
|
|
continue;
|
|
|
|
}
|
2011-01-26 06:50:47 +08:00
|
|
|
|
2011-01-26 12:49:52 +08:00
|
|
|
CFGStmt CS = cast<CFGStmt>(*ri);
|
2011-08-24 07:05:04 +08:00
|
|
|
const Stmt *S = CS.getStmt();
|
2010-03-21 05:06:02 +08:00
|
|
|
if (isa<ReturnStmt>(S)) {
|
|
|
|
HasLiveReturn = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (isa<ObjCAtThrowStmt>(S)) {
|
|
|
|
HasFakeEdge = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (isa<CXXThrowExpr>(S)) {
|
|
|
|
HasFakeEdge = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (const AsmStmt *AS = dyn_cast<AsmStmt>(S)) {
|
|
|
|
if (AS->isMSAsm()) {
|
|
|
|
HasFakeEdge = true;
|
|
|
|
HasLiveReturn = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (isa<CXXTryStmt>(S)) {
|
|
|
|
HasAbnormalEdge = true;
|
|
|
|
continue;
|
|
|
|
}
|
2011-09-13 17:53:58 +08:00
|
|
|
if (std::find(B.succ_begin(), B.succ_end(), &cfg->getExit())
|
|
|
|
== B.succ_end()) {
|
|
|
|
HasAbnormalEdge = true;
|
|
|
|
continue;
|
2010-03-21 05:06:02 +08:00
|
|
|
}
|
2011-09-13 17:53:58 +08:00
|
|
|
|
|
|
|
HasPlainEdge = true;
|
2010-03-21 05:06:02 +08:00
|
|
|
}
|
|
|
|
if (!HasPlainEdge) {
|
|
|
|
if (HasLiveReturn)
|
|
|
|
return NeverFallThrough;
|
|
|
|
return NeverFallThroughOrReturn;
|
|
|
|
}
|
|
|
|
if (HasAbnormalEdge || HasFakeEdge || HasLiveReturn)
|
|
|
|
return MaybeFallThrough;
|
|
|
|
// This says AlwaysFallThrough for calls to functions that are not marked
|
|
|
|
// noreturn, that don't return. If people would like this warning to be more
|
|
|
|
// accurate, such functions should be marked as noreturn.
|
|
|
|
return AlwaysFallThrough;
|
|
|
|
}
|
|
|
|
|
2010-07-27 05:25:24 +08:00
|
|
|
namespace {
|
|
|
|
|
2010-03-21 05:06:02 +08:00
|
|
|
struct CheckFallThroughDiagnostics {
|
|
|
|
unsigned diag_MaybeFallThrough_HasNoReturn;
|
|
|
|
unsigned diag_MaybeFallThrough_ReturnsNonVoid;
|
|
|
|
unsigned diag_AlwaysFallThrough_HasNoReturn;
|
|
|
|
unsigned diag_AlwaysFallThrough_ReturnsNonVoid;
|
|
|
|
unsigned diag_NeverFallThroughOrReturn;
|
|
|
|
bool funMode;
|
2010-12-16 02:44:22 +08:00
|
|
|
SourceLocation FuncLoc;
|
2010-03-23 08:13:23 +08:00
|
|
|
|
2010-04-17 07:28:44 +08:00
|
|
|
static CheckFallThroughDiagnostics MakeForFunction(const Decl *Func) {
|
2010-03-21 05:06:02 +08:00
|
|
|
CheckFallThroughDiagnostics D;
|
2010-12-16 02:44:22 +08:00
|
|
|
D.FuncLoc = Func->getLocation();
|
2010-03-21 05:06:02 +08:00
|
|
|
D.diag_MaybeFallThrough_HasNoReturn =
|
|
|
|
diag::warn_falloff_noreturn_function;
|
|
|
|
D.diag_MaybeFallThrough_ReturnsNonVoid =
|
|
|
|
diag::warn_maybe_falloff_nonvoid_function;
|
|
|
|
D.diag_AlwaysFallThrough_HasNoReturn =
|
|
|
|
diag::warn_falloff_noreturn_function;
|
|
|
|
D.diag_AlwaysFallThrough_ReturnsNonVoid =
|
|
|
|
diag::warn_falloff_nonvoid_function;
|
2010-04-17 07:28:44 +08:00
|
|
|
|
|
|
|
// Don't suggest that virtual functions be marked "noreturn", since they
|
|
|
|
// might be overridden by non-noreturn functions.
|
|
|
|
bool isVirtualMethod = false;
|
|
|
|
if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Func))
|
|
|
|
isVirtualMethod = Method->isVirtual();
|
|
|
|
|
2011-10-11 02:15:57 +08:00
|
|
|
// Don't suggest that template instantiations be marked "noreturn"
|
|
|
|
bool isTemplateInstantiation = false;
|
2011-12-01 08:59:17 +08:00
|
|
|
if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(Func))
|
|
|
|
isTemplateInstantiation = Function->isTemplateInstantiation();
|
2011-10-11 02:15:57 +08:00
|
|
|
|
|
|
|
if (!isVirtualMethod && !isTemplateInstantiation)
|
2010-04-17 07:28:44 +08:00
|
|
|
D.diag_NeverFallThroughOrReturn =
|
|
|
|
diag::warn_suggest_noreturn_function;
|
|
|
|
else
|
|
|
|
D.diag_NeverFallThroughOrReturn = 0;
|
|
|
|
|
2010-03-21 05:06:02 +08:00
|
|
|
D.funMode = true;
|
|
|
|
return D;
|
|
|
|
}
|
2010-03-23 08:13:23 +08:00
|
|
|
|
2010-03-21 05:06:02 +08:00
|
|
|
static CheckFallThroughDiagnostics MakeForBlock() {
|
|
|
|
CheckFallThroughDiagnostics D;
|
|
|
|
D.diag_MaybeFallThrough_HasNoReturn =
|
|
|
|
diag::err_noreturn_block_has_return_expr;
|
|
|
|
D.diag_MaybeFallThrough_ReturnsNonVoid =
|
|
|
|
diag::err_maybe_falloff_nonvoid_block;
|
|
|
|
D.diag_AlwaysFallThrough_HasNoReturn =
|
|
|
|
diag::err_noreturn_block_has_return_expr;
|
|
|
|
D.diag_AlwaysFallThrough_ReturnsNonVoid =
|
|
|
|
diag::err_falloff_nonvoid_block;
|
|
|
|
D.diag_NeverFallThroughOrReturn =
|
|
|
|
diag::warn_suggest_noreturn_block;
|
|
|
|
D.funMode = false;
|
|
|
|
return D;
|
|
|
|
}
|
2010-03-23 08:13:23 +08:00
|
|
|
|
2011-09-26 07:23:43 +08:00
|
|
|
bool checkDiagnostics(DiagnosticsEngine &D, bool ReturnsVoid,
|
2010-03-21 05:06:02 +08:00
|
|
|
bool HasNoReturn) const {
|
|
|
|
if (funMode) {
|
2010-12-16 02:44:22 +08:00
|
|
|
return (ReturnsVoid ||
|
|
|
|
D.getDiagnosticLevel(diag::warn_maybe_falloff_nonvoid_function,
|
2011-09-26 07:23:43 +08:00
|
|
|
FuncLoc) == DiagnosticsEngine::Ignored)
|
2010-12-16 02:44:22 +08:00
|
|
|
&& (!HasNoReturn ||
|
|
|
|
D.getDiagnosticLevel(diag::warn_noreturn_function_has_return_expr,
|
2011-09-26 07:23:43 +08:00
|
|
|
FuncLoc) == DiagnosticsEngine::Ignored)
|
2010-12-16 02:44:22 +08:00
|
|
|
&& (!ReturnsVoid ||
|
|
|
|
D.getDiagnosticLevel(diag::warn_suggest_noreturn_block, FuncLoc)
|
2011-09-26 07:23:43 +08:00
|
|
|
== DiagnosticsEngine::Ignored);
|
2010-03-21 05:06:02 +08:00
|
|
|
}
|
2010-03-23 08:13:23 +08:00
|
|
|
|
2010-03-21 05:06:02 +08:00
|
|
|
// For blocks.
|
|
|
|
return ReturnsVoid && !HasNoReturn
|
2010-12-16 02:44:22 +08:00
|
|
|
&& (!ReturnsVoid ||
|
|
|
|
D.getDiagnosticLevel(diag::warn_suggest_noreturn_block, FuncLoc)
|
2011-09-26 07:23:43 +08:00
|
|
|
== DiagnosticsEngine::Ignored);
|
2010-03-21 05:06:02 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2010-07-27 05:25:24 +08:00
|
|
|
}
|
|
|
|
|
2010-03-21 05:06:02 +08:00
|
|
|
/// CheckFallThroughForFunctionDef - Check that we don't fall off the end of a
|
|
|
|
/// function that should return a value. Check that we don't fall off the end
|
|
|
|
/// of a noreturn function. We assume that functions and blocks not marked
|
|
|
|
/// noreturn will return.
|
|
|
|
static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body,
|
2011-02-23 09:51:48 +08:00
|
|
|
const BlockExpr *blkExpr,
|
2010-03-21 05:06:02 +08:00
|
|
|
const CheckFallThroughDiagnostics& CD,
|
2011-10-24 09:32:45 +08:00
|
|
|
AnalysisDeclContext &AC) {
|
2010-03-21 05:06:02 +08:00
|
|
|
|
|
|
|
bool ReturnsVoid = false;
|
|
|
|
bool HasNoReturn = false;
|
|
|
|
|
|
|
|
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
|
|
|
|
ReturnsVoid = FD->getResultType()->isVoidType();
|
|
|
|
HasNoReturn = FD->hasAttr<NoReturnAttr>() ||
|
2010-03-31 04:24:48 +08:00
|
|
|
FD->getType()->getAs<FunctionType>()->getNoReturnAttr();
|
2010-03-21 05:06:02 +08:00
|
|
|
}
|
|
|
|
else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
|
|
|
|
ReturnsVoid = MD->getResultType()->isVoidType();
|
|
|
|
HasNoReturn = MD->hasAttr<NoReturnAttr>();
|
|
|
|
}
|
|
|
|
else if (isa<BlockDecl>(D)) {
|
2011-02-23 09:51:48 +08:00
|
|
|
QualType BlockTy = blkExpr->getType();
|
2010-03-23 08:13:23 +08:00
|
|
|
if (const FunctionType *FT =
|
2010-03-21 05:06:02 +08:00
|
|
|
BlockTy->getPointeeType()->getAs<FunctionType>()) {
|
|
|
|
if (FT->getResultType()->isVoidType())
|
|
|
|
ReturnsVoid = true;
|
|
|
|
if (FT->getNoReturnAttr())
|
|
|
|
HasNoReturn = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-26 07:23:43 +08:00
|
|
|
DiagnosticsEngine &Diags = S.getDiagnostics();
|
2010-03-21 05:06:02 +08:00
|
|
|
|
|
|
|
// Short circuit for compilation speed.
|
|
|
|
if (CD.checkDiagnostics(Diags, ReturnsVoid, HasNoReturn))
|
|
|
|
return;
|
2010-03-23 08:13:23 +08:00
|
|
|
|
2010-03-21 05:06:02 +08:00
|
|
|
// FIXME: Function try block
|
|
|
|
if (const CompoundStmt *Compound = dyn_cast<CompoundStmt>(Body)) {
|
|
|
|
switch (CheckFallThrough(AC)) {
|
2010-05-16 17:34:11 +08:00
|
|
|
case UnknownFallThrough:
|
|
|
|
break;
|
|
|
|
|
2010-03-21 05:06:02 +08:00
|
|
|
case MaybeFallThrough:
|
|
|
|
if (HasNoReturn)
|
|
|
|
S.Diag(Compound->getRBracLoc(),
|
|
|
|
CD.diag_MaybeFallThrough_HasNoReturn);
|
|
|
|
else if (!ReturnsVoid)
|
|
|
|
S.Diag(Compound->getRBracLoc(),
|
|
|
|
CD.diag_MaybeFallThrough_ReturnsNonVoid);
|
|
|
|
break;
|
|
|
|
case AlwaysFallThrough:
|
|
|
|
if (HasNoReturn)
|
|
|
|
S.Diag(Compound->getRBracLoc(),
|
|
|
|
CD.diag_AlwaysFallThrough_HasNoReturn);
|
|
|
|
else if (!ReturnsVoid)
|
|
|
|
S.Diag(Compound->getRBracLoc(),
|
|
|
|
CD.diag_AlwaysFallThrough_ReturnsNonVoid);
|
|
|
|
break;
|
|
|
|
case NeverFallThroughOrReturn:
|
2011-08-31 17:01:53 +08:00
|
|
|
if (ReturnsVoid && !HasNoReturn && CD.diag_NeverFallThroughOrReturn) {
|
|
|
|
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
|
|
|
|
S.Diag(Compound->getLBracLoc(), CD.diag_NeverFallThroughOrReturn)
|
2011-09-10 08:56:20 +08:00
|
|
|
<< 0 << FD;
|
|
|
|
} else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
|
|
|
|
S.Diag(Compound->getLBracLoc(), CD.diag_NeverFallThroughOrReturn)
|
|
|
|
<< 1 << MD;
|
2011-08-31 17:01:53 +08:00
|
|
|
} else {
|
|
|
|
S.Diag(Compound->getLBracLoc(), CD.diag_NeverFallThroughOrReturn);
|
|
|
|
}
|
|
|
|
}
|
2010-03-21 05:06:02 +08:00
|
|
|
break;
|
|
|
|
case NeverFallThrough:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-15 10:58:47 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// -Wuninitialized
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2011-04-05 04:56:00 +08:00
|
|
|
namespace {
|
2011-04-05 14:48:00 +08:00
|
|
|
/// ContainsReference - A visitor class to search for references to
|
|
|
|
/// a particular declaration (the needle) within any evaluated component of an
|
|
|
|
/// expression (recursively).
|
2011-04-05 04:56:00 +08:00
|
|
|
class ContainsReference : public EvaluatedExprVisitor<ContainsReference> {
|
2011-04-05 14:48:00 +08:00
|
|
|
bool FoundReference;
|
|
|
|
const DeclRefExpr *Needle;
|
|
|
|
|
2011-04-05 04:56:00 +08:00
|
|
|
public:
|
2011-04-05 14:48:00 +08:00
|
|
|
ContainsReference(ASTContext &Context, const DeclRefExpr *Needle)
|
|
|
|
: EvaluatedExprVisitor<ContainsReference>(Context),
|
|
|
|
FoundReference(false), Needle(Needle) {}
|
|
|
|
|
|
|
|
void VisitExpr(Expr *E) {
|
2011-04-05 04:56:00 +08:00
|
|
|
// Stop evaluating if we already have a reference.
|
2011-04-05 14:48:00 +08:00
|
|
|
if (FoundReference)
|
2011-04-05 04:56:00 +08:00
|
|
|
return;
|
2011-04-05 14:48:00 +08:00
|
|
|
|
|
|
|
EvaluatedExprVisitor<ContainsReference>::VisitExpr(E);
|
2011-04-05 04:56:00 +08:00
|
|
|
}
|
2011-04-05 14:48:00 +08:00
|
|
|
|
|
|
|
void VisitDeclRefExpr(DeclRefExpr *E) {
|
|
|
|
if (E == Needle)
|
|
|
|
FoundReference = true;
|
|
|
|
else
|
|
|
|
EvaluatedExprVisitor<ContainsReference>::VisitDeclRefExpr(E);
|
2011-04-05 04:56:00 +08:00
|
|
|
}
|
2011-04-05 14:48:00 +08:00
|
|
|
|
|
|
|
bool doesContainReference() const { return FoundReference; }
|
2011-04-05 04:56:00 +08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2011-09-10 13:35:08 +08:00
|
|
|
static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) {
|
|
|
|
// Don't issue a fixit if there is already an initializer.
|
|
|
|
if (VD->getInit())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Suggest possible initialization (if any).
|
|
|
|
QualType VariableTy = VD->getType().getCanonicalType();
|
Improve 0-argument -Wvexing-parse diagnostic by adding notes with fix-its:
- If the declarator is at the start of a line, and the previous line contained
another declarator and ended with a comma, then that comma was probably a
typo for a semicolon:
int n = 0, m = 1, l = 2, // k = 5;
myImportantFunctionCall(); // oops!
- If removing the parentheses would correctly initialize the object, then
produce a note suggesting that fix.
- Otherwise, if there is a simple initializer we can suggest which performs
value-initialization, then provide a note suggesting a correction to that
initializer.
Sema::Declarator now tracks the location of the comma prior to the declarator in
the declaration, if there is one, to facilitate providing the note. The code to
determine an appropriate initializer from the -Wuninitialized warning has been
factored out to allow use in both that and -Wvexing-parse.
llvm-svn: 148072
2012-01-13 07:53:29 +08:00
|
|
|
const char *Init = S.getFixItZeroInitializerForType(VariableTy);
|
|
|
|
if (!Init)
|
2011-09-10 13:35:08 +08:00
|
|
|
return false;
|
Improve 0-argument -Wvexing-parse diagnostic by adding notes with fix-its:
- If the declarator is at the start of a line, and the previous line contained
another declarator and ended with a comma, then that comma was probably a
typo for a semicolon:
int n = 0, m = 1, l = 2, // k = 5;
myImportantFunctionCall(); // oops!
- If removing the parentheses would correctly initialize the object, then
produce a note suggesting that fix.
- Otherwise, if there is a simple initializer we can suggest which performs
value-initialization, then provide a note suggesting a correction to that
initializer.
Sema::Declarator now tracks the location of the comma prior to the declarator in
the declaration, if there is one, to facilitate providing the note. The code to
determine an appropriate initializer from the -Wuninitialized warning has been
factored out to allow use in both that and -Wvexing-parse.
llvm-svn: 148072
2012-01-13 07:53:29 +08:00
|
|
|
|
|
|
|
SourceLocation Loc = S.PP.getLocForEndOfToken(VD->getLocEnd());
|
|
|
|
S.Diag(Loc, diag::note_var_fixit_add_initialization) << VD->getDeclName()
|
|
|
|
<< FixItHint::CreateInsertion(Loc, Init);
|
|
|
|
return true;
|
2011-09-10 13:35:08 +08:00
|
|
|
}
|
|
|
|
|
2011-04-06 02:27:05 +08:00
|
|
|
/// DiagnoseUninitializedUse -- Helper function for diagnosing uses of an
|
|
|
|
/// uninitialized variable. This manages the different forms of diagnostic
|
|
|
|
/// emitted for particular types of uses. Returns true if the use was diagnosed
|
|
|
|
/// as a warning. If a pariticular use is one we omit warnings for, returns
|
|
|
|
/// false.
|
|
|
|
static bool DiagnoseUninitializedUse(Sema &S, const VarDecl *VD,
|
2011-10-14 02:50:06 +08:00
|
|
|
const Expr *E, bool isAlwaysUninit,
|
|
|
|
bool alwaysReportSelfInit = false) {
|
2011-04-06 02:18:05 +08:00
|
|
|
bool isSelfInit = false;
|
|
|
|
|
|
|
|
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
|
|
|
|
if (isAlwaysUninit) {
|
|
|
|
// Inspect the initializer of the variable declaration which is
|
|
|
|
// being referenced prior to its initialization. We emit
|
|
|
|
// specialized diagnostics for self-initialization, and we
|
|
|
|
// specifically avoid warning about self references which take the
|
|
|
|
// form of:
|
|
|
|
//
|
|
|
|
// int x = x;
|
|
|
|
//
|
|
|
|
// This is used to indicate to GCC that 'x' is intentionally left
|
|
|
|
// uninitialized. Proven code paths which access 'x' in
|
|
|
|
// an uninitialized state after this will still warn.
|
|
|
|
//
|
|
|
|
// TODO: Should we suppress maybe-uninitialized warnings for
|
|
|
|
// variables initialized in this way?
|
|
|
|
if (const Expr *Initializer = VD->getInit()) {
|
2011-10-14 02:50:06 +08:00
|
|
|
if (!alwaysReportSelfInit && DRE == Initializer->IgnoreParenImpCasts())
|
2011-04-06 02:27:05 +08:00
|
|
|
return false;
|
2011-04-06 02:18:05 +08:00
|
|
|
|
|
|
|
ContainsReference CR(S.Context, DRE);
|
|
|
|
CR.Visit(const_cast<Expr*>(Initializer));
|
|
|
|
isSelfInit = CR.doesContainReference();
|
|
|
|
}
|
|
|
|
if (isSelfInit) {
|
|
|
|
S.Diag(DRE->getLocStart(),
|
|
|
|
diag::warn_uninit_self_reference_in_init)
|
|
|
|
<< VD->getDeclName() << VD->getLocation() << DRE->getSourceRange();
|
|
|
|
} else {
|
|
|
|
S.Diag(DRE->getLocStart(), diag::warn_uninit_var)
|
|
|
|
<< VD->getDeclName() << DRE->getSourceRange();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
S.Diag(DRE->getLocStart(), diag::warn_maybe_uninit_var)
|
|
|
|
<< VD->getDeclName() << DRE->getSourceRange();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const BlockExpr *BE = cast<BlockExpr>(E);
|
|
|
|
S.Diag(BE->getLocStart(),
|
|
|
|
isAlwaysUninit ? diag::warn_uninit_var_captured_by_block
|
|
|
|
: diag::warn_maybe_uninit_var_captured_by_block)
|
|
|
|
<< VD->getDeclName();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Report where the variable was declared when the use wasn't within
|
2011-09-10 13:35:08 +08:00
|
|
|
// the initializer of that declaration & we didn't already suggest
|
|
|
|
// an initialization fixit.
|
|
|
|
if (!isSelfInit && !SuggestInitializationFixit(S, VD))
|
2011-04-06 02:18:05 +08:00
|
|
|
S.Diag(VD->getLocStart(), diag::note_uninit_var_def)
|
|
|
|
<< VD->getDeclName();
|
|
|
|
|
2011-04-06 02:27:05 +08:00
|
|
|
return true;
|
2011-04-06 02:18:08 +08:00
|
|
|
}
|
|
|
|
|
2011-03-15 12:57:38 +08:00
|
|
|
typedef std::pair<const Expr*, bool> UninitUse;
|
|
|
|
|
2011-01-15 10:58:47 +08:00
|
|
|
namespace {
|
2011-01-22 03:41:41 +08:00
|
|
|
struct SLocSort {
|
2011-03-15 12:57:38 +08:00
|
|
|
bool operator()(const UninitUse &a, const UninitUse &b) {
|
|
|
|
SourceLocation aLoc = a.first->getLocStart();
|
|
|
|
SourceLocation bLoc = b.first->getLocStart();
|
2011-01-22 03:41:41 +08:00
|
|
|
return aLoc.getRawEncoding() < bLoc.getRawEncoding();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-01-15 10:58:47 +08:00
|
|
|
class UninitValsDiagReporter : public UninitVariablesHandler {
|
|
|
|
Sema &S;
|
2011-07-23 18:55:15 +08:00
|
|
|
typedef SmallVector<UninitUse, 2> UsesVec;
|
2011-10-14 02:50:06 +08:00
|
|
|
typedef llvm::DenseMap<const VarDecl *, std::pair<UsesVec*, bool> > UsesMap;
|
2011-01-22 03:41:41 +08:00
|
|
|
UsesMap *uses;
|
|
|
|
|
2011-01-15 10:58:47 +08:00
|
|
|
public:
|
2011-01-22 03:41:41 +08:00
|
|
|
UninitValsDiagReporter(Sema &S) : S(S), uses(0) {}
|
|
|
|
~UninitValsDiagReporter() {
|
|
|
|
flushDiagnostics();
|
|
|
|
}
|
2011-10-14 02:50:06 +08:00
|
|
|
|
|
|
|
std::pair<UsesVec*, bool> &getUses(const VarDecl *vd) {
|
2011-01-22 03:41:41 +08:00
|
|
|
if (!uses)
|
|
|
|
uses = new UsesMap();
|
2011-10-14 02:50:06 +08:00
|
|
|
|
|
|
|
UsesMap::mapped_type &V = (*uses)[vd];
|
|
|
|
UsesVec *&vec = V.first;
|
2011-01-22 03:41:41 +08:00
|
|
|
if (!vec)
|
|
|
|
vec = new UsesVec();
|
|
|
|
|
2011-10-14 02:50:06 +08:00
|
|
|
return V;
|
|
|
|
}
|
|
|
|
|
|
|
|
void handleUseOfUninitVariable(const Expr *ex, const VarDecl *vd,
|
|
|
|
bool isAlwaysUninit) {
|
|
|
|
getUses(vd).first->push_back(std::make_pair(ex, isAlwaysUninit));
|
|
|
|
}
|
|
|
|
|
|
|
|
void handleSelfInit(const VarDecl *vd) {
|
|
|
|
getUses(vd).second = true;
|
2011-01-22 03:41:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void flushDiagnostics() {
|
|
|
|
if (!uses)
|
|
|
|
return;
|
2011-02-03 07:35:53 +08:00
|
|
|
|
2011-01-22 03:41:41 +08:00
|
|
|
for (UsesMap::iterator i = uses->begin(), e = uses->end(); i != e; ++i) {
|
|
|
|
const VarDecl *vd = i->first;
|
2011-10-14 02:50:06 +08:00
|
|
|
const UsesMap::mapped_type &V = i->second;
|
|
|
|
|
|
|
|
UsesVec *vec = V.first;
|
|
|
|
bool hasSelfInit = V.second;
|
|
|
|
|
|
|
|
// 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.
|
2011-10-20 02:53:03 +08:00
|
|
|
if (!vec->empty() && hasSelfInit && hasAlwaysUninitializedUse(vec))
|
2011-10-14 02:50:06 +08:00
|
|
|
DiagnoseUninitializedUse(S, vd, vd->getInit()->IgnoreParenCasts(),
|
2011-10-20 02:53:03 +08:00
|
|
|
/* isAlwaysUninit */ true,
|
|
|
|
/* alwaysReportSelfInit */ true);
|
2011-10-14 02:50:06 +08:00
|
|
|
else {
|
|
|
|
// Sort the uses by their SourceLocations. While not strictly
|
|
|
|
// guaranteed to produce them in line/column order, this will provide
|
|
|
|
// a stable ordering.
|
|
|
|
std::sort(vec->begin(), vec->end(), SLocSort());
|
|
|
|
|
|
|
|
for (UsesVec::iterator vi = vec->begin(), ve = vec->end(); vi != ve;
|
|
|
|
++vi) {
|
|
|
|
if (DiagnoseUninitializedUse(S, vd, vi->first,
|
|
|
|
/*isAlwaysUninit=*/vi->second))
|
|
|
|
// Skip further diagnostics for this variable. We try to warn only
|
|
|
|
// on the first point at which a variable is used uninitialized.
|
|
|
|
break;
|
|
|
|
}
|
2011-04-06 02:18:08 +08:00
|
|
|
}
|
2011-10-14 02:50:06 +08:00
|
|
|
|
|
|
|
// Release the uses vector.
|
2011-01-22 03:41:41 +08:00
|
|
|
delete vec;
|
|
|
|
}
|
|
|
|
delete uses;
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
2011-10-20 02:53:03 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
static bool hasAlwaysUninitializedUse(const UsesVec* vec) {
|
|
|
|
for (UsesVec::const_iterator i = vec->begin(), e = vec->end(); i != e; ++i) {
|
|
|
|
if (i->second) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2011-01-15 10:58:47 +08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2011-08-24 02:46:34 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// -Wthread-safety
|
|
|
|
//===----------------------------------------------------------------------===//
|
2011-09-10 00:04:02 +08:00
|
|
|
namespace clang {
|
|
|
|
namespace thread_safety {
|
|
|
|
typedef std::pair<SourceLocation, PartialDiagnostic> DelayedDiag;
|
|
|
|
typedef llvm::SmallVector<DelayedDiag, 4> DiagList;
|
|
|
|
|
|
|
|
struct SortDiagBySourceLocation {
|
|
|
|
Sema &S;
|
|
|
|
SortDiagBySourceLocation(Sema &S) : S(S) {}
|
|
|
|
|
|
|
|
bool operator()(const DelayedDiag &left, const DelayedDiag &right) {
|
|
|
|
// Although this call will be slow, this is only called when outputting
|
|
|
|
// multiple warnings.
|
|
|
|
return S.getSourceManager().isBeforeInTranslationUnit(left.first,
|
|
|
|
right.first);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-12-20 10:48:34 +08:00
|
|
|
namespace {
|
2011-09-10 00:04:02 +08:00
|
|
|
class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler {
|
|
|
|
Sema &S;
|
|
|
|
DiagList Warnings;
|
2011-10-22 02:10:14 +08:00
|
|
|
SourceLocation FunLocation;
|
2011-09-10 00:04:02 +08:00
|
|
|
|
|
|
|
// Helper functions
|
|
|
|
void warnLockMismatch(unsigned DiagID, Name LockName, SourceLocation Loc) {
|
2011-10-22 02:10:14 +08:00
|
|
|
// Gracefully handle rare cases when the analysis can't get a more
|
|
|
|
// precise source location.
|
|
|
|
if (!Loc.isValid())
|
|
|
|
Loc = FunLocation;
|
2011-09-10 00:04:02 +08:00
|
|
|
PartialDiagnostic Warning = S.PDiag(DiagID) << LockName;
|
|
|
|
Warnings.push_back(DelayedDiag(Loc, Warning));
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
2011-10-22 02:10:14 +08:00
|
|
|
ThreadSafetyReporter(Sema &S, SourceLocation FL)
|
|
|
|
: S(S), FunLocation(FL) {}
|
2011-09-10 00:04:02 +08:00
|
|
|
|
|
|
|
/// \brief Emit all buffered diagnostics in order of sourcelocation.
|
|
|
|
/// We need to output diagnostics produced while iterating through
|
|
|
|
/// the lockset in deterministic order, so this function orders diagnostics
|
|
|
|
/// and outputs them.
|
|
|
|
void emitDiagnostics() {
|
|
|
|
SortDiagBySourceLocation SortDiagBySL(S);
|
|
|
|
sort(Warnings.begin(), Warnings.end(), SortDiagBySL);
|
|
|
|
for (DiagList::iterator I = Warnings.begin(), E = Warnings.end();
|
|
|
|
I != E; ++I)
|
|
|
|
S.Diag(I->first, I->second);
|
|
|
|
}
|
|
|
|
|
2011-09-10 00:21:55 +08:00
|
|
|
void handleInvalidLockExp(SourceLocation Loc) {
|
|
|
|
PartialDiagnostic Warning = S.PDiag(diag::warn_cannot_resolve_lock) << Loc;
|
|
|
|
Warnings.push_back(DelayedDiag(Loc, Warning));
|
|
|
|
}
|
2011-09-10 00:04:02 +08:00
|
|
|
void handleUnmatchedUnlock(Name LockName, SourceLocation Loc) {
|
|
|
|
warnLockMismatch(diag::warn_unlock_but_no_lock, LockName, Loc);
|
|
|
|
}
|
|
|
|
|
|
|
|
void handleDoubleLock(Name LockName, SourceLocation Loc) {
|
|
|
|
warnLockMismatch(diag::warn_double_lock, LockName, Loc);
|
|
|
|
}
|
|
|
|
|
2011-09-16 01:25:19 +08:00
|
|
|
void handleMutexHeldEndOfScope(Name LockName, SourceLocation Loc,
|
|
|
|
LockErrorKind LEK){
|
|
|
|
unsigned DiagID = 0;
|
|
|
|
switch (LEK) {
|
|
|
|
case LEK_LockedSomePredecessors:
|
|
|
|
DiagID = diag::warn_lock_at_end_of_scope;
|
|
|
|
break;
|
|
|
|
case LEK_LockedSomeLoopIterations:
|
|
|
|
DiagID = diag::warn_expecting_lock_held_on_loop;
|
|
|
|
break;
|
|
|
|
case LEK_LockedAtEndOfFunction:
|
|
|
|
DiagID = diag::warn_no_unlock;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
warnLockMismatch(DiagID, LockName, Loc);
|
2011-09-10 00:04:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void handleExclusiveAndShared(Name LockName, SourceLocation Loc1,
|
|
|
|
SourceLocation Loc2) {
|
|
|
|
PartialDiagnostic Warning =
|
|
|
|
S.PDiag(diag::warn_lock_exclusive_and_shared) << LockName;
|
|
|
|
PartialDiagnostic Note =
|
|
|
|
S.PDiag(diag::note_lock_exclusive_and_shared) << LockName;
|
|
|
|
Warnings.push_back(DelayedDiag(Loc1, Warning));
|
|
|
|
Warnings.push_back(DelayedDiag(Loc2, Note));
|
|
|
|
}
|
|
|
|
|
|
|
|
void handleNoMutexHeld(const NamedDecl *D, ProtectedOperationKind POK,
|
|
|
|
AccessKind AK, SourceLocation Loc) {
|
2011-09-15 04:09:09 +08:00
|
|
|
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;
|
|
|
|
PartialDiagnostic Warning = S.PDiag(DiagID)
|
|
|
|
<< D->getName() << getLockKindFromAccessKind(AK);
|
|
|
|
Warnings.push_back(DelayedDiag(Loc, Warning));
|
2011-09-10 00:04:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void handleMutexNotHeld(const NamedDecl *D, ProtectedOperationKind POK,
|
|
|
|
Name LockName, LockKind LK, SourceLocation Loc) {
|
2011-09-14 02:01:58 +08:00
|
|
|
unsigned DiagID = 0;
|
2011-09-10 00:04:02 +08:00
|
|
|
switch (POK) {
|
|
|
|
case POK_VarAccess:
|
|
|
|
DiagID = diag::warn_variable_requires_lock;
|
|
|
|
break;
|
|
|
|
case POK_VarDereference:
|
|
|
|
DiagID = diag::warn_var_deref_requires_lock;
|
|
|
|
break;
|
|
|
|
case POK_FunctionCall:
|
|
|
|
DiagID = diag::warn_fun_requires_lock;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
PartialDiagnostic Warning = S.PDiag(DiagID)
|
2011-09-15 04:09:09 +08:00
|
|
|
<< D->getName() << LockName << LK;
|
2011-09-10 00:04:02 +08:00
|
|
|
Warnings.push_back(DelayedDiag(Loc, Warning));
|
|
|
|
}
|
|
|
|
|
|
|
|
void handleFunExcludesLock(Name FunName, Name LockName, SourceLocation Loc) {
|
|
|
|
PartialDiagnostic Warning =
|
|
|
|
S.PDiag(diag::warn_fun_excludes_mutex) << FunName << LockName;
|
|
|
|
Warnings.push_back(DelayedDiag(Loc, Warning));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2011-12-20 10:48:34 +08:00
|
|
|
}
|
2011-09-10 00:04:02 +08:00
|
|
|
|
2010-03-21 05:06:02 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AnalysisBasedWarnings - Worker object used by Sema to execute analysis-based
|
|
|
|
// warnings on a function, method, or block.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2010-03-23 08:13:23 +08:00
|
|
|
clang::sema::AnalysisBasedWarnings::Policy::Policy() {
|
2010-03-21 05:06:02 +08:00
|
|
|
enableCheckFallThrough = 1;
|
2010-03-23 08:13:23 +08:00
|
|
|
enableCheckUnreachable = 0;
|
2011-08-24 02:46:34 +08:00
|
|
|
enableThreadSafetyAnalysis = 0;
|
2010-03-23 08:13:23 +08:00
|
|
|
}
|
2010-03-21 05:06:02 +08:00
|
|
|
|
2011-07-07 00:21:37 +08:00
|
|
|
clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s)
|
|
|
|
: S(s),
|
|
|
|
NumFunctionsAnalyzed(0),
|
2011-07-09 04:38:53 +08:00
|
|
|
NumFunctionsWithBadCFGs(0),
|
2011-07-07 00:21:37 +08:00
|
|
|
NumCFGBlocks(0),
|
2011-07-09 04:38:53 +08:00
|
|
|
MaxCFGBlocksPerFunction(0),
|
|
|
|
NumUninitAnalysisFunctions(0),
|
|
|
|
NumUninitAnalysisVariables(0),
|
|
|
|
MaxUninitAnalysisVariablesPerFunction(0),
|
|
|
|
NumUninitAnalysisBlockVisits(0),
|
|
|
|
MaxUninitAnalysisBlockVisitsPerFunction(0) {
|
2011-09-26 07:23:43 +08:00
|
|
|
DiagnosticsEngine &D = S.getDiagnostics();
|
2010-03-23 08:13:23 +08:00
|
|
|
DefaultPolicy.enableCheckUnreachable = (unsigned)
|
2010-12-16 02:44:22 +08:00
|
|
|
(D.getDiagnosticLevel(diag::warn_unreachable, SourceLocation()) !=
|
2011-09-26 07:23:43 +08:00
|
|
|
DiagnosticsEngine::Ignored);
|
2011-08-24 02:46:34 +08:00
|
|
|
DefaultPolicy.enableThreadSafetyAnalysis = (unsigned)
|
|
|
|
(D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) !=
|
2011-09-26 07:23:43 +08:00
|
|
|
DiagnosticsEngine::Ignored);
|
2011-08-24 02:46:34 +08:00
|
|
|
|
2010-03-21 05:06:02 +08:00
|
|
|
}
|
|
|
|
|
2011-02-23 09:52:04 +08:00
|
|
|
static void flushDiagnostics(Sema &S, sema::FunctionScopeInfo *fscope) {
|
2011-07-23 18:55:15 +08:00
|
|
|
for (SmallVectorImpl<sema::PossiblyUnreachableDiag>::iterator
|
2011-02-23 09:52:04 +08:00
|
|
|
i = fscope->PossiblyUnreachableDiags.begin(),
|
|
|
|
e = fscope->PossiblyUnreachableDiags.end();
|
|
|
|
i != e; ++i) {
|
|
|
|
const sema::PossiblyUnreachableDiag &D = *i;
|
|
|
|
S.Diag(D.Loc, D.PD);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-23 08:13:23 +08:00
|
|
|
void clang::sema::
|
|
|
|
AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
|
2011-02-23 09:51:53 +08:00
|
|
|
sema::FunctionScopeInfo *fscope,
|
2011-02-23 09:51:48 +08:00
|
|
|
const Decl *D, const BlockExpr *blkExpr) {
|
2010-03-21 05:11:09 +08:00
|
|
|
|
2010-03-21 05:06:02 +08:00
|
|
|
// We avoid doing analysis-based warnings when there are errors for
|
|
|
|
// two reasons:
|
|
|
|
// (1) The CFGs often can't be constructed (if the body is invalid), so
|
|
|
|
// don't bother trying.
|
|
|
|
// (2) The code already has problems; running the analysis just takes more
|
|
|
|
// time.
|
2011-09-26 07:23:43 +08:00
|
|
|
DiagnosticsEngine &Diags = S.getDiagnostics();
|
2010-05-01 05:49:25 +08:00
|
|
|
|
2010-03-23 08:13:23 +08:00
|
|
|
// Do not do any analysis for declarations in system headers if we are
|
|
|
|
// going to just ignore them.
|
2010-05-01 05:49:25 +08:00
|
|
|
if (Diags.getSuppressSystemWarnings() &&
|
2010-03-23 08:13:23 +08:00
|
|
|
S.SourceMgr.isInSystemHeader(D->getLocation()))
|
|
|
|
return;
|
|
|
|
|
2010-08-25 13:56:39 +08:00
|
|
|
// For code in dependent contexts, we'll do this at instantiation time.
|
2012-01-24 12:51:48 +08:00
|
|
|
if (cast<DeclContext>(D)->isDependentContext())
|
|
|
|
return;
|
2010-03-21 05:06:02 +08:00
|
|
|
|
2011-02-23 09:52:04 +08:00
|
|
|
if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) {
|
|
|
|
// Flush out any possibly unreachable diagnostics.
|
|
|
|
flushDiagnostics(S, fscope);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-03-21 05:06:02 +08:00
|
|
|
const Stmt *Body = D->getBody();
|
|
|
|
assert(Body);
|
|
|
|
|
2011-10-24 09:32:45 +08:00
|
|
|
AnalysisDeclContext AC(/* AnalysisDeclContextManager */ 0, D, 0);
|
2011-07-21 13:22:47 +08:00
|
|
|
|
2010-03-21 05:06:02 +08:00
|
|
|
// Don't generate EH edges for CallExprs as we'd like to avoid the n^2
|
|
|
|
// explosion for destrutors that can result and the compile time hit.
|
2011-07-21 13:22:47 +08:00
|
|
|
AC.getCFGBuildOptions().PruneTriviallyFalseEdges = true;
|
|
|
|
AC.getCFGBuildOptions().AddEHEdges = false;
|
|
|
|
AC.getCFGBuildOptions().AddInitializers = true;
|
|
|
|
AC.getCFGBuildOptions().AddImplicitDtors = true;
|
2011-07-19 22:18:48 +08:00
|
|
|
|
|
|
|
// Force that certain expressions appear as CFGElements in the CFG. This
|
|
|
|
// is used to speed up various analyses.
|
|
|
|
// FIXME: This isn't the right factoring. This is here for initial
|
|
|
|
// prototyping, but we need a way for analyses to say what expressions they
|
|
|
|
// expect to always be CFGElements and then fill in the BuildOptions
|
|
|
|
// appropriately. This is essentially a layering violation.
|
2011-12-09 04:23:06 +08:00
|
|
|
if (P.enableCheckUnreachable || P.enableThreadSafetyAnalysis) {
|
|
|
|
// Unreachable code analysis and thread safety require a linearized CFG.
|
2011-08-24 07:05:11 +08:00
|
|
|
AC.getCFGBuildOptions().setAllAlwaysAdd();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
AC.getCFGBuildOptions()
|
|
|
|
.setAlwaysAdd(Stmt::BinaryOperatorClass)
|
|
|
|
.setAlwaysAdd(Stmt::BlockExprClass)
|
|
|
|
.setAlwaysAdd(Stmt::CStyleCastExprClass)
|
|
|
|
.setAlwaysAdd(Stmt::DeclRefExprClass)
|
|
|
|
.setAlwaysAdd(Stmt::ImplicitCastExprClass)
|
|
|
|
.setAlwaysAdd(Stmt::UnaryOperatorClass);
|
|
|
|
}
|
2011-07-21 13:22:47 +08:00
|
|
|
|
|
|
|
// Construct the analysis context with the specified CFG build options.
|
|
|
|
|
2011-02-23 09:52:04 +08:00
|
|
|
// Emit delayed diagnostics.
|
2012-01-24 12:51:48 +08:00
|
|
|
if (!fscope->PossiblyUnreachableDiags.empty()) {
|
2011-02-23 09:52:04 +08:00
|
|
|
bool analyzed = false;
|
2011-03-10 11:50:34 +08:00
|
|
|
|
|
|
|
// Register the expressions with the CFGBuilder.
|
2011-07-23 18:55:15 +08:00
|
|
|
for (SmallVectorImpl<sema::PossiblyUnreachableDiag>::iterator
|
2011-03-10 11:50:34 +08:00
|
|
|
i = fscope->PossiblyUnreachableDiags.begin(),
|
|
|
|
e = fscope->PossiblyUnreachableDiags.end();
|
|
|
|
i != e; ++i) {
|
|
|
|
if (const Stmt *stmt = i->stmt)
|
|
|
|
AC.registerForcedBlockExpression(stmt);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (AC.getCFG()) {
|
|
|
|
analyzed = true;
|
2011-07-23 18:55:15 +08:00
|
|
|
for (SmallVectorImpl<sema::PossiblyUnreachableDiag>::iterator
|
2011-03-10 11:50:34 +08:00
|
|
|
i = fscope->PossiblyUnreachableDiags.begin(),
|
|
|
|
e = fscope->PossiblyUnreachableDiags.end();
|
|
|
|
i != e; ++i)
|
|
|
|
{
|
|
|
|
const sema::PossiblyUnreachableDiag &D = *i;
|
|
|
|
bool processed = false;
|
|
|
|
if (const Stmt *stmt = i->stmt) {
|
|
|
|
const CFGBlock *block = AC.getBlockForRegisteredExpression(stmt);
|
2012-01-21 09:01:51 +08:00
|
|
|
CFGReverseBlockReachabilityAnalysis *cra =
|
|
|
|
AC.getCFGReachablityAnalysis();
|
|
|
|
// FIXME: We should be able to assert that block is non-null, but
|
|
|
|
// the CFG analysis can skip potentially-evaluated expressions in
|
|
|
|
// edge cases; see test/Sema/vla-2.c.
|
|
|
|
if (block && cra) {
|
2011-02-23 09:52:04 +08:00
|
|
|
// Can this block be reached from the entrance?
|
2011-03-10 11:50:34 +08:00
|
|
|
if (cra->isReachable(&AC.getCFG()->getEntry(), block))
|
2011-02-23 09:52:04 +08:00
|
|
|
S.Diag(D.Loc, D.PD);
|
2011-03-10 11:50:34 +08:00
|
|
|
processed = true;
|
2011-02-23 09:52:04 +08:00
|
|
|
}
|
2011-03-10 11:50:34 +08:00
|
|
|
}
|
|
|
|
if (!processed) {
|
|
|
|
// Emit the warning anyway if we cannot map to a basic block.
|
|
|
|
S.Diag(D.Loc, D.PD);
|
2011-02-23 09:52:04 +08:00
|
|
|
}
|
|
|
|
}
|
2011-03-10 11:50:34 +08:00
|
|
|
}
|
2011-02-23 09:52:04 +08:00
|
|
|
|
|
|
|
if (!analyzed)
|
|
|
|
flushDiagnostics(S, fscope);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-21 05:06:02 +08:00
|
|
|
// Warning: check missing 'return'
|
2012-01-24 12:51:48 +08:00
|
|
|
if (P.enableCheckFallThrough) {
|
2010-03-21 05:06:02 +08:00
|
|
|
const CheckFallThroughDiagnostics &CD =
|
|
|
|
(isa<BlockDecl>(D) ? CheckFallThroughDiagnostics::MakeForBlock()
|
2010-04-17 07:28:44 +08:00
|
|
|
: CheckFallThroughDiagnostics::MakeForFunction(D));
|
2011-02-23 09:51:48 +08:00
|
|
|
CheckFallThroughForBody(S, D, Body, blkExpr, CD, AC);
|
2010-03-21 05:06:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Warning: check for unreachable code
|
2011-12-01 05:22:09 +08:00
|
|
|
if (P.enableCheckUnreachable) {
|
|
|
|
// Only check for unreachable code on non-template instantiations.
|
|
|
|
// Different template instantiations can effectively change the control-flow
|
|
|
|
// and it is very difficult to prove that a snippet of code in a template
|
|
|
|
// is unreachable for all instantiations.
|
2011-12-01 08:59:17 +08:00
|
|
|
bool isTemplateInstantiation = false;
|
|
|
|
if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(D))
|
|
|
|
isTemplateInstantiation = Function->isTemplateInstantiation();
|
|
|
|
if (!isTemplateInstantiation)
|
2011-12-01 05:22:09 +08:00
|
|
|
CheckUnreachable(S, AC);
|
|
|
|
}
|
2011-09-10 00:04:02 +08:00
|
|
|
|
2011-08-24 02:46:34 +08:00
|
|
|
// Check for thread safety violations
|
2012-01-24 12:51:48 +08:00
|
|
|
if (P.enableThreadSafetyAnalysis) {
|
2011-10-22 02:10:14 +08:00
|
|
|
SourceLocation FL = AC.getDecl()->getLocation();
|
|
|
|
thread_safety::ThreadSafetyReporter Reporter(S, FL);
|
2011-09-10 00:04:02 +08:00
|
|
|
thread_safety::runThreadSafetyAnalysis(AC, Reporter);
|
|
|
|
Reporter.emitDiagnostics();
|
|
|
|
}
|
2011-08-24 02:46:34 +08:00
|
|
|
|
2011-01-26 03:13:48 +08:00
|
|
|
if (Diags.getDiagnosticLevel(diag::warn_uninit_var, D->getLocStart())
|
2011-09-26 07:23:43 +08:00
|
|
|
!= DiagnosticsEngine::Ignored ||
|
2011-03-15 13:22:28 +08:00
|
|
|
Diags.getDiagnosticLevel(diag::warn_maybe_uninit_var, D->getLocStart())
|
2011-09-26 07:23:43 +08:00
|
|
|
!= DiagnosticsEngine::Ignored) {
|
2011-03-17 13:29:57 +08:00
|
|
|
if (CFG *cfg = AC.getCFG()) {
|
2011-01-19 05:18:58 +08:00
|
|
|
UninitValsDiagReporter reporter(S);
|
2011-07-17 02:31:33 +08:00
|
|
|
UninitVariablesAnalysisStats stats;
|
2011-07-17 04:13:06 +08:00
|
|
|
std::memset(&stats, 0, sizeof(UninitVariablesAnalysisStats));
|
2011-01-26 03:13:48 +08:00
|
|
|
runUninitializedVariablesAnalysis(*cast<DeclContext>(D), *cfg, AC,
|
2011-07-07 00:21:37 +08:00
|
|
|
reporter, stats);
|
|
|
|
|
|
|
|
if (S.CollectStats && stats.NumVariablesAnalyzed > 0) {
|
|
|
|
++NumUninitAnalysisFunctions;
|
|
|
|
NumUninitAnalysisVariables += stats.NumVariablesAnalyzed;
|
|
|
|
NumUninitAnalysisBlockVisits += stats.NumBlockVisits;
|
|
|
|
MaxUninitAnalysisVariablesPerFunction =
|
|
|
|
std::max(MaxUninitAnalysisVariablesPerFunction,
|
|
|
|
stats.NumVariablesAnalyzed);
|
|
|
|
MaxUninitAnalysisBlockVisitsPerFunction =
|
|
|
|
std::max(MaxUninitAnalysisBlockVisitsPerFunction,
|
|
|
|
stats.NumBlockVisits);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Collect statistics about the CFG if it was built.
|
|
|
|
if (S.CollectStats && AC.isCFGBuilt()) {
|
|
|
|
++NumFunctionsAnalyzed;
|
|
|
|
if (CFG *cfg = AC.getCFG()) {
|
|
|
|
// If we successfully built a CFG for this context, record some more
|
|
|
|
// detail information about it.
|
2011-07-07 06:21:45 +08:00
|
|
|
NumCFGBlocks += cfg->getNumBlockIDs();
|
2011-07-07 00:21:37 +08:00
|
|
|
MaxCFGBlocksPerFunction = std::max(MaxCFGBlocksPerFunction,
|
2011-07-07 06:21:45 +08:00
|
|
|
cfg->getNumBlockIDs());
|
2011-07-07 00:21:37 +08:00
|
|
|
} else {
|
|
|
|
++NumFunctionsWithBadCFGs;
|
2011-01-15 10:58:47 +08:00
|
|
|
}
|
|
|
|
}
|
2010-03-21 05:06:02 +08:00
|
|
|
}
|
2011-07-07 00:21:37 +08:00
|
|
|
|
|
|
|
void clang::sema::AnalysisBasedWarnings::PrintStats() const {
|
|
|
|
llvm::errs() << "\n*** Analysis Based Warnings Stats:\n";
|
|
|
|
|
|
|
|
unsigned NumCFGsBuilt = NumFunctionsAnalyzed - NumFunctionsWithBadCFGs;
|
|
|
|
unsigned AvgCFGBlocksPerFunction =
|
|
|
|
!NumCFGsBuilt ? 0 : NumCFGBlocks/NumCFGsBuilt;
|
|
|
|
llvm::errs() << NumFunctionsAnalyzed << " functions analyzed ("
|
|
|
|
<< NumFunctionsWithBadCFGs << " w/o CFGs).\n"
|
|
|
|
<< " " << NumCFGBlocks << " CFG blocks built.\n"
|
|
|
|
<< " " << AvgCFGBlocksPerFunction
|
|
|
|
<< " average CFG blocks per function.\n"
|
|
|
|
<< " " << MaxCFGBlocksPerFunction
|
|
|
|
<< " max CFG blocks per function.\n";
|
|
|
|
|
|
|
|
unsigned AvgUninitVariablesPerFunction = !NumUninitAnalysisFunctions ? 0
|
|
|
|
: NumUninitAnalysisVariables/NumUninitAnalysisFunctions;
|
|
|
|
unsigned AvgUninitBlockVisitsPerFunction = !NumUninitAnalysisFunctions ? 0
|
|
|
|
: NumUninitAnalysisBlockVisits/NumUninitAnalysisFunctions;
|
|
|
|
llvm::errs() << NumUninitAnalysisFunctions
|
|
|
|
<< " functions analyzed for uninitialiazed variables\n"
|
|
|
|
<< " " << NumUninitAnalysisVariables << " variables analyzed.\n"
|
|
|
|
<< " " << AvgUninitVariablesPerFunction
|
|
|
|
<< " average variables per function.\n"
|
|
|
|
<< " " << MaxUninitAnalysisVariablesPerFunction
|
|
|
|
<< " max variables per function.\n"
|
|
|
|
<< " " << NumUninitAnalysisBlockVisits << " block visits.\n"
|
|
|
|
<< " " << AvgUninitBlockVisitsPerFunction
|
|
|
|
<< " average block visits per function.\n"
|
|
|
|
<< " " << MaxUninitAnalysisBlockVisitsPerFunction
|
|
|
|
<< " max block visits per function.\n";
|
|
|
|
}
|