Newer
Older
Ted Kremenek
committed
//=- 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.
//
//===----------------------------------------------------------------------===//
#include "clang/Sema/AnalysisBasedWarnings.h"
John McCall
committed
#include "clang/Sema/SemaInternal.h"
Ted Kremenek
committed
#include "clang/Sema/ScopeInfo.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/AST/DeclObjC.h"
Ted Kremenek
committed
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/EvaluatedExprVisitor.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/RecursiveASTVisitor.h"
Ted Kremenek
committed
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/Analyses/ReachableCode.h"
Ted Kremenek
committed
#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
#include "clang/Analysis/Analyses/ThreadSafety.h"
Ted Kremenek
committed
#include "clang/Analysis/CFGStmtMap.h"
#include "clang/Analysis/Analyses/UninitializedValues.h"
Ted Kremenek
committed
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
Ted Kremenek
committed
#include "llvm/Support/Casting.h"
#include <algorithm>
#include <iterator>
#include <vector>
#include <deque>
Ted Kremenek
committed
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.
static void CheckUnreachable(Sema &S, AnalysisDeclContext &AC) {
Ted Kremenek
committed
UnreachableCodeHandler UC(S);
reachable_code::FindUnreachableCode(AC, UC);
}
//===----------------------------------------------------------------------===//
// Check for missing return value.
//===----------------------------------------------------------------------===//
enum ControlFlowKind {
UnknownFallThrough,
NeverFallThrough,
MaybeFallThrough,
AlwaysFallThrough,
NeverFallThroughOrReturn
};
Ted Kremenek
committed
/// 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.
static ControlFlowKind CheckFallThrough(AnalysisDeclContext &AC) {
Ted Kremenek
committed
CFG *cfg = AC.getCFG();
if (cfg == 0) return UnknownFallThrough;
Ted Kremenek
committed
// 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());
Ted Kremenek
committed
unsigned count = reachable_code::ScanReachableFromBlock(&cfg->getEntry(),
Ted Kremenek
committed
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.
Ted Kremenek
committed
count += reachable_code::ScanReachableFromBlock(&b, live);
Ted Kremenek
committed
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;
Ted Kremenek
committed
// 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;
Ted Kremenek
committed
if (!live[B.getBlockID()])
continue;
// 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;
}
// 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();
for ( ; ri != re ; ++ri)
if (isa<CFGStmt>(*ri))
// No more CFGElements in the block?
if (ri == re) {
Ted Kremenek
committed
if (B.getTerminator() && isa<CXXTryStmt>(B.getTerminator())) {
HasAbnormalEdge = true;
continue;
}
// A labeled empty statement, or the entry block...
HasPlainEdge = true;
continue;
}
CFGStmt CS = cast<CFGStmt>(*ri);
Ted Kremenek
committed
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;
}
if (std::find(B.succ_begin(), B.succ_end(), &cfg->getExit())
== B.succ_end()) {
HasAbnormalEdge = true;
continue;
Ted Kremenek
committed
}
HasPlainEdge = true;
Ted Kremenek
committed
}
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;
}
Ted Kremenek
committed
struct CheckFallThroughDiagnostics {
unsigned diag_MaybeFallThrough_HasNoReturn;
unsigned diag_MaybeFallThrough_ReturnsNonVoid;
unsigned diag_AlwaysFallThrough_HasNoReturn;
unsigned diag_AlwaysFallThrough_ReturnsNonVoid;
unsigned diag_NeverFallThroughOrReturn;
enum { Function, Block, Lambda } funMode;
static CheckFallThroughDiagnostics MakeForFunction(const Decl *Func) {
Ted Kremenek
committed
CheckFallThroughDiagnostics D;
Ted Kremenek
committed
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;
// 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();
// Don't suggest that template instantiations be marked "noreturn"
bool isTemplateInstantiation = false;
Ted Kremenek
committed
if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(Func))
isTemplateInstantiation = Function->isTemplateInstantiation();
if (!isVirtualMethod && !isTemplateInstantiation)
D.diag_NeverFallThroughOrReturn =
diag::warn_suggest_noreturn_function;
else
D.diag_NeverFallThroughOrReturn = 0;
D.funMode = Function;
Ted Kremenek
committed
return D;
}
Ted Kremenek
committed
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 = Block;
return D;
}
static CheckFallThroughDiagnostics MakeForLambda() {
CheckFallThroughDiagnostics D;
D.diag_MaybeFallThrough_HasNoReturn =
diag::err_noreturn_lambda_has_return_expr;
D.diag_MaybeFallThrough_ReturnsNonVoid =
diag::warn_maybe_falloff_nonvoid_lambda;
D.diag_AlwaysFallThrough_HasNoReturn =
diag::err_noreturn_lambda_has_return_expr;
D.diag_AlwaysFallThrough_ReturnsNonVoid =
diag::warn_falloff_nonvoid_lambda;
D.diag_NeverFallThroughOrReturn = 0;
D.funMode = Lambda;
Ted Kremenek
committed
return D;
}
bool checkDiagnostics(DiagnosticsEngine &D, bool ReturnsVoid,
Ted Kremenek
committed
bool HasNoReturn) const {
if (funMode == Function) {
return (ReturnsVoid ||
D.getDiagnosticLevel(diag::warn_maybe_falloff_nonvoid_function,
FuncLoc) == DiagnosticsEngine::Ignored)
&& (!HasNoReturn ||
D.getDiagnosticLevel(diag::warn_noreturn_function_has_return_expr,
FuncLoc) == DiagnosticsEngine::Ignored)
&& (!ReturnsVoid ||
D.getDiagnosticLevel(diag::warn_suggest_noreturn_block, FuncLoc)
== DiagnosticsEngine::Ignored);
Ted Kremenek
committed
}
// For blocks / lambdas.
return ReturnsVoid && !HasNoReturn
&& ((funMode == Lambda) ||
D.getDiagnosticLevel(diag::warn_suggest_noreturn_block, FuncLoc)
== DiagnosticsEngine::Ignored);
Ted Kremenek
committed
}
};
Ted Kremenek
committed
/// 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,
const BlockExpr *blkExpr,
Ted Kremenek
committed
const CheckFallThroughDiagnostics& CD,
AnalysisDeclContext &AC) {
Ted Kremenek
committed
bool ReturnsVoid = false;
bool HasNoReturn = false;
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
ReturnsVoid = FD->getResultType()->isVoidType();
HasNoReturn = FD->hasAttr<NoReturnAttr>() ||
FD->getType()->getAs<FunctionType>()->getNoReturnAttr();
Ted Kremenek
committed
}
else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
ReturnsVoid = MD->getResultType()->isVoidType();
HasNoReturn = MD->hasAttr<NoReturnAttr>();
}
else if (isa<BlockDecl>(D)) {
QualType BlockTy = blkExpr->getType();
if (const FunctionType *FT =
Ted Kremenek
committed
BlockTy->getPointeeType()->getAs<FunctionType>()) {
if (FT->getResultType()->isVoidType())
ReturnsVoid = true;
if (FT->getNoReturnAttr())
HasNoReturn = true;
}
}
DiagnosticsEngine &Diags = S.getDiagnostics();
Ted Kremenek
committed
// Short circuit for compilation speed.
if (CD.checkDiagnostics(Diags, ReturnsVoid, HasNoReturn))
return;
Ted Kremenek
committed
// FIXME: Function try block
if (const CompoundStmt *Compound = dyn_cast<CompoundStmt>(Body)) {
switch (CheckFallThrough(AC)) {
case UnknownFallThrough:
break;
Ted Kremenek
committed
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:
if (ReturnsVoid && !HasNoReturn && CD.diag_NeverFallThroughOrReturn) {
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
S.Diag(Compound->getLBracLoc(), CD.diag_NeverFallThroughOrReturn)
<< 0 << FD;
} else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
S.Diag(Compound->getLBracLoc(), CD.diag_NeverFallThroughOrReturn)
<< 1 << MD;
} else {
S.Diag(Compound->getLBracLoc(), CD.diag_NeverFallThroughOrReturn);
}
}
Ted Kremenek
committed
break;
case NeverFallThrough:
break;
}
}
}
//===----------------------------------------------------------------------===//
// -Wuninitialized
//===----------------------------------------------------------------------===//
namespace {
/// ContainsReference - A visitor class to search for references to
/// a particular declaration (the needle) within any evaluated component of an
/// expression (recursively).
class ContainsReference : public EvaluatedExprVisitor<ContainsReference> {
bool FoundReference;
const DeclRefExpr *Needle;
public:
ContainsReference(ASTContext &Context, const DeclRefExpr *Needle)
: EvaluatedExprVisitor<ContainsReference>(Context),
FoundReference(false), Needle(Needle) {}
void VisitExpr(Expr *E) {
// Stop evaluating if we already have a reference.
if (FoundReference)
return;
EvaluatedExprVisitor<ContainsReference>::VisitExpr(E);
}
void VisitDeclRefExpr(DeclRefExpr *E) {
if (E == Needle)
FoundReference = true;
else
EvaluatedExprVisitor<ContainsReference>::VisitDeclRefExpr(E);
}
bool doesContainReference() const { return FoundReference; }
};
}
David Blaikie
committed
static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) {
QualType VariableTy = VD->getType().getCanonicalType();
if (VariableTy->isBlockPointerType() &&
!VD->hasAttr<BlocksAttr>()) {
S.Diag(VD->getLocation(), diag::note_block_var_fixit_add_initialization) << VD->getDeclName()
<< FixItHint::CreateInsertion(VD->getLocation(), "__block ");
return true;
}
David Blaikie
committed
// Don't issue a fixit if there is already an initializer.
if (VD->getInit())
return false;
David Blaikie
committed
// Suggest possible initialization (if any).
David Blaikie
committed
std::string Init = S.getFixItZeroInitializerForType(VariableTy);
if (Init.empty())
David Blaikie
committed
return false;
// Don't suggest a fixit inside macros.
if (VD->getLocEnd().isMacroID())
return false;
SourceLocation Loc = S.PP.getLocForEndOfToken(VD->getLocEnd());
S.Diag(Loc, diag::note_var_fixit_add_initialization) << VD->getDeclName()
<< FixItHint::CreateInsertion(Loc, Init);
return true;
David Blaikie
committed
}
/// 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,
Ted Kremenek
committed
const Expr *E, bool isAlwaysUninit,
bool alwaysReportSelfInit = false) {
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
// 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.
if (const Expr *Initializer = VD->getInit()) {
if (!alwaysReportSelfInit && DRE == Initializer->IgnoreParenImpCasts())
return false;
ContainsReference CR(S.Context, DRE);
CR.Visit(const_cast<Expr*>(Initializer));
if (CR.doesContainReference()) {
S.Diag(DRE->getLocStart(),
diag::warn_uninit_self_reference_in_init)
<< VD->getDeclName() << VD->getLocation() << DRE->getSourceRange();
return true;
}
}
S.Diag(DRE->getLocStart(), isAlwaysUninit ? diag::warn_uninit_var
: diag::warn_maybe_uninit_var)
<< VD->getDeclName() << DRE->getSourceRange();
} else {
const BlockExpr *BE = cast<BlockExpr>(E);
if (VD->getType()->isBlockPointerType() &&
!VD->hasAttr<BlocksAttr>())
S.Diag(BE->getLocStart(), diag::warn_uninit_byref_blockvar_captured_by_block)
<< VD->getDeclName();
else
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
David Blaikie
committed
// the initializer of that declaration & we didn't already suggest
// an initialization fixit.
if (!SuggestInitializationFixit(S, VD))
S.Diag(VD->getLocStart(), diag::note_uninit_var_def)
<< VD->getDeclName();
return true;
}
namespace {
class FallthroughMapper : public RecursiveASTVisitor<FallthroughMapper> {
public:
FallthroughMapper(Sema &S)
: FoundSwitchStatements(false),
S(S) {
}
bool foundSwitchStatements() const { return FoundSwitchStatements; }
void markFallthroughVisited(const AttributedStmt *Stmt) {
bool Found = FallthroughStmts.erase(Stmt);
assert(Found);
(void)Found;
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
}
typedef llvm::SmallPtrSet<const AttributedStmt*, 8> AttrStmts;
const AttrStmts &getFallthroughStmts() const {
return FallthroughStmts;
}
bool checkFallThroughIntoBlock(const CFGBlock &B, int &AnnotatedCnt) {
int UnannotatedCnt = 0;
AnnotatedCnt = 0;
std::deque<const CFGBlock*> BlockQueue;
std::copy(B.pred_begin(), B.pred_end(), std::back_inserter(BlockQueue));
while (!BlockQueue.empty()) {
const CFGBlock *P = BlockQueue.front();
BlockQueue.pop_front();
const Stmt *Term = P->getTerminator();
if (Term && isa<SwitchStmt>(Term))
continue; // Switch statement, good.
const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(P->getLabel());
if (SW && SW->getSubStmt() == B.getLabel() && P->begin() == P->end())
continue; // Previous case label has no statements, good.
if (P->pred_begin() == P->pred_end()) { // The block is unreachable.
// This only catches trivially unreachable blocks.
for (CFGBlock::const_iterator ElIt = P->begin(), ElEnd = P->end();
ElIt != ElEnd; ++ElIt) {
if (const CFGStmt *CS = ElIt->getAs<CFGStmt>()){
if (const AttributedStmt *AS = asFallThroughAttr(CS->getStmt())) {
S.Diag(AS->getLocStart(),
diag::warn_fallthrough_attr_unreachable);
markFallthroughVisited(AS);
++AnnotatedCnt;
}
// Don't care about other unreachable statements.
}
}
// If there are no unreachable statements, this may be a special
// case in CFG:
// case X: {
// A a; // A has a destructor.
// break;
// }
// // <<<< This place is represented by a 'hanging' CFG block.
// case Y:
continue;
}
const Stmt *LastStmt = getLastStmt(*P);
if (const AttributedStmt *AS = asFallThroughAttr(LastStmt)) {
markFallthroughVisited(AS);
++AnnotatedCnt;
continue; // Fallthrough annotation, good.
}
if (!LastStmt) { // This block contains no executable statements.
// Traverse its predecessors.
std::copy(P->pred_begin(), P->pred_end(),
std::back_inserter(BlockQueue));
continue;
}
++UnannotatedCnt;
}
return !!UnannotatedCnt;
}
// RecursiveASTVisitor setup.
bool shouldWalkTypesOfTypeLocs() const { return false; }
bool VisitAttributedStmt(AttributedStmt *S) {
if (asFallThroughAttr(S))
FallthroughStmts.insert(S);
return true;
}
bool VisitSwitchStmt(SwitchStmt *S) {
FoundSwitchStatements = true;
return true;
}
private:
static const AttributedStmt *asFallThroughAttr(const Stmt *S) {
if (const AttributedStmt *AS = dyn_cast_or_null<AttributedStmt>(S)) {
if (hasSpecificAttr<FallThroughAttr>(AS->getAttrs()))
return AS;
}
return 0;
}
static const Stmt *getLastStmt(const CFGBlock &B) {
if (const Stmt *Term = B.getTerminator())
return Term;
for (CFGBlock::const_reverse_iterator ElemIt = B.rbegin(),
ElemEnd = B.rend();
ElemIt != ElemEnd; ++ElemIt) {
if (const CFGStmt *CS = ElemIt->getAs<CFGStmt>())
return CS->getStmt();
}
// Workaround to detect a statement thrown out by CFGBuilder:
// case X: {} case Y:
// case X: ; case Y:
if (const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(B.getLabel()))
if (!isa<SwitchCase>(SW->getSubStmt()))
return SW->getSubStmt();
return 0;
}
bool FoundSwitchStatements;
AttrStmts FallthroughStmts;
Sema &S;
};
}
static void DiagnoseSwitchLabelsFallthrough(Sema &S, AnalysisDeclContext &AC) {
FallthroughMapper FM(S);
FM.TraverseStmt(AC.getBody());
if (!FM.foundSwitchStatements())
return;
CFG *Cfg = AC.getCFG();
if (!Cfg)
return;
int AnnotatedCnt;
for (CFG::reverse_iterator I = Cfg->rbegin(), E = Cfg->rend(); I != E; ++I) {
const CFGBlock &B = **I;
const Stmt *Label = B.getLabel();
if (!Label || !isa<SwitchCase>(Label))
continue;
if (!FM.checkFallThroughIntoBlock(B, AnnotatedCnt))
continue;
S.Diag(Label->getLocStart(), diag::warn_unannotated_fallthrough);
if (!AnnotatedCnt) {
SourceLocation L = Label->getLocStart();
if (L.isMacroID())
continue;
if (S.getLangOpts().CPlusPlus0x) {
S.Diag(L, diag::note_insert_fallthrough_fixit) <<
FixItHint::CreateInsertion(L, "[[clang::fallthrough]]; ");
}
S.Diag(L, diag::note_insert_break_fixit) <<
FixItHint::CreateInsertion(L, "break; ");
}
}
const FallthroughMapper::AttrStmts &Fallthroughs = FM.getFallthroughStmts();
for (FallthroughMapper::AttrStmts::const_iterator I = Fallthroughs.begin(),
E = Fallthroughs.end();
I != E; ++I) {
S.Diag((*I)->getLocStart(), diag::warn_fallthrough_attr_invalid_placement);
}
}
Ted Kremenek
committed
typedef std::pair<const Expr*, bool> UninitUse;
Ted Kremenek
committed
bool operator()(const UninitUse &a, const UninitUse &b) {
SourceLocation aLoc = a.first->getLocStart();
SourceLocation bLoc = b.first->getLocStart();
return aLoc.getRawEncoding() < bLoc.getRawEncoding();
}
};
class UninitValsDiagReporter : public UninitVariablesHandler {
Sema &S;
Chris Lattner
committed
typedef SmallVector<UninitUse, 2> UsesVec;
Ted Kremenek
committed
typedef llvm::DenseMap<const VarDecl *, std::pair<UsesVec*, bool> > UsesMap;
UninitValsDiagReporter(Sema &S) : S(S), uses(0) {}
~UninitValsDiagReporter() {
flushDiagnostics();
}
Ted Kremenek
committed
std::pair<UsesVec*, bool> &getUses(const VarDecl *vd) {
if (!uses)
uses = new UsesMap();
Ted Kremenek
committed
UsesMap::mapped_type &V = (*uses)[vd];
UsesVec *&vec = V.first;
if (!vec)
vec = new UsesVec();
Ted Kremenek
committed
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;
}
void flushDiagnostics() {
if (!uses)
return;
Ted Kremenek
committed
Richard Smith
committed
// FIXME: This iteration order, and thus the resulting diagnostic order,
// is nondeterministic.
for (UsesMap::iterator i = uses->begin(), e = uses->end(); i != e; ++i) {
const VarDecl *vd = i->first;
Ted Kremenek
committed
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.
Matt Beaumont-Gay
committed
if (!vec->empty() && hasSelfInit && hasAlwaysUninitializedUse(vec))
Ted Kremenek
committed
DiagnoseUninitializedUse(S, vd, vd->getInit()->IgnoreParenCasts(),
Matt Beaumont-Gay
committed
/* isAlwaysUninit */ true,
/* alwaysReportSelfInit */ true);
Ted Kremenek
committed
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;
}
}
Ted Kremenek
committed
// Release the uses vector.
delete vec;
}
delete uses;
Matt Beaumont-Gay
committed
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;
}
//===----------------------------------------------------------------------===//
// -Wthread-safety
//===----------------------------------------------------------------------===//
namespace clang {
namespace thread_safety {
typedef llvm::SmallVector<PartialDiagnosticAt, 1> OptionalNotes;
typedef std::pair<PartialDiagnosticAt, OptionalNotes> DelayedDiag;
typedef std::list<DelayedDiag> DiagList;
struct SortDiagBySourceLocation {
SourceManager &SM;
SortDiagBySourceLocation(SourceManager &SM) : SM(SM) {}
bool operator()(const DelayedDiag &left, const DelayedDiag &right) {
// Although this call will be slow, this is only called when outputting
// multiple warnings.
return SM.isBeforeInTranslationUnit(left.first.first, right.first.first);
David Blaikie
committed
namespace {
class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler {
Sema &S;
DiagList Warnings;
// Helper functions
void warnLockMismatch(unsigned DiagID, Name LockName, SourceLocation Loc) {
// Gracefully handle rare cases when the analysis can't get a more
// precise source location.
if (!Loc.isValid())
Loc = FunLocation;
PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID) << LockName);
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
ThreadSafetyReporter(Sema &S, SourceLocation FL, SourceLocation FEL)
: S(S), FunLocation(FL), FunEndLocation(FEL) {}
/// \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() {
Warnings.sort(SortDiagBySourceLocation(S.getSourceManager()));
for (DiagList::iterator I = Warnings.begin(), E = Warnings.end();
I != E; ++I) {
S.Diag(I->first.first, I->first.second);
const OptionalNotes &Notes = I->second;
for (unsigned NoteI = 0, NoteN = Notes.size(); NoteI != NoteN; ++NoteI)
S.Diag(Notes[NoteI].first, Notes[NoteI].second);
}
Caitlin Sadowski
committed
void handleInvalidLockExp(SourceLocation Loc) {
PartialDiagnosticAt Warning(Loc,
S.PDiag(diag::warn_cannot_resolve_lock) << Loc);
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
Caitlin Sadowski
committed
}
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);
}
void handleMutexHeldEndOfScope(Name LockName, SourceLocation LocLocked,
SourceLocation LocEndOfScope,
Caitlin Sadowski
committed
LockErrorKind LEK){
unsigned DiagID = 0;
switch (LEK) {
case LEK_LockedSomePredecessors:
Caitlin Sadowski
committed
break;
case LEK_LockedSomeLoopIterations:
DiagID = diag::warn_expecting_lock_held_on_loop;
break;
case LEK_LockedAtEndOfFunction:
DiagID = diag::warn_no_unlock;
break;
}
if (LocEndOfScope.isInvalid())
LocEndOfScope = FunEndLocation;
PartialDiagnosticAt Warning(LocEndOfScope, S.PDiag(DiagID) << LockName);
PartialDiagnosticAt Note(LocLocked, S.PDiag(diag::note_locked_here));
Warnings.push_back(DelayedDiag(Warning, OptionalNotes(1, Note)));
}
void handleExclusiveAndShared(Name LockName, SourceLocation Loc1,
SourceLocation Loc2) {
PartialDiagnosticAt Warning(
Loc1, S.PDiag(diag::warn_lock_exclusive_and_shared) << LockName);
PartialDiagnosticAt Note(
Loc2, S.PDiag(diag::note_lock_exclusive_and_shared) << LockName);
Warnings.push_back(DelayedDiag(Warning, OptionalNotes(1, Note)));
}
void handleNoMutexHeld(const NamedDecl *D, ProtectedOperationKind POK,
AccessKind AK, SourceLocation Loc) {
Caitlin Sadowski
committed
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;
PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID)
<< D->getName() << getLockKindFromAccessKind(AK));
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
}
void handleMutexNotHeld(const NamedDecl *D, ProtectedOperationKind POK,
Name LockName, LockKind LK, SourceLocation Loc) {
Caitlin Sadowski
committed
unsigned DiagID = 0;
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;
}
PartialDiagnosticAt Warning(Loc, S.PDiag(DiagID)
<< D->getName() << LockName << LK);
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
}
void handleFunExcludesLock(Name FunName, Name LockName, SourceLocation Loc) {
PartialDiagnosticAt Warning(Loc,
S.PDiag(diag::warn_fun_excludes_mutex) << FunName << LockName);
Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
}
};
}
}
David Blaikie
committed
}
Ted Kremenek
committed
//===----------------------------------------------------------------------===//
// AnalysisBasedWarnings - Worker object used by Sema to execute analysis-based
// warnings on a function, method, or block.
//===----------------------------------------------------------------------===//
clang::sema::AnalysisBasedWarnings::Policy::Policy() {
Ted Kremenek
committed
enableCheckFallThrough = 1;
enableCheckUnreachable = 0;
enableThreadSafetyAnalysis = 0;
Ted Kremenek
committed
clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s)
: S(s),
NumFunctionsAnalyzed(0),
NumFunctionsWithBadCFGs(0),
NumCFGBlocks(0),
MaxCFGBlocksPerFunction(0),
NumUninitAnalysisFunctions(0),
NumUninitAnalysisVariables(0),
MaxUninitAnalysisVariablesPerFunction(0),
NumUninitAnalysisBlockVisits(0),
MaxUninitAnalysisBlockVisitsPerFunction(0) {
DiagnosticsEngine &D = S.getDiagnostics();
DefaultPolicy.enableCheckUnreachable = (unsigned)
(D.getDiagnosticLevel(diag::warn_unreachable, SourceLocation()) !=
DiagnosticsEngine::Ignored);
DefaultPolicy.enableThreadSafetyAnalysis = (unsigned)
(D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) !=
DiagnosticsEngine::Ignored);
Ted Kremenek
committed
}
Ted Kremenek
committed
static void flushDiagnostics(Sema &S, sema::FunctionScopeInfo *fscope) {
Chris Lattner
committed
for (SmallVectorImpl<sema::PossiblyUnreachableDiag>::iterator
Ted Kremenek
committed
i = fscope->PossiblyUnreachableDiags.begin(),
e = fscope->PossiblyUnreachableDiags.end();
i != e; ++i) {
const sema::PossiblyUnreachableDiag &D = *i;
S.Diag(D.Loc, D.PD);
}
}
void clang::sema::
AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
sema::FunctionScopeInfo *fscope,
const Decl *D, const BlockExpr *blkExpr) {
Ted Kremenek
committed
// 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.
DiagnosticsEngine &Diags = S.getDiagnostics();
Ted Kremenek
committed
// Do not do any analysis for declarations in system headers if we are
// going to just ignore them.