Skip to content

Commit

Permalink
[analyzer][UninitializedObjectChecker] New flag to ignore records bas…
Browse files Browse the repository at this point in the history
…ed on it's fields

Based on a suggestion from @george.karpenkov.

In some cases, structs are used as unions with a help of a tag/kind field.
This patch adds a new string flag (a pattern), that is matched against the
fields of a record, and should a match be found, the entire record is ignored.

For more info refer to http://lists.llvm.org/pipermail/cfe-dev/2018-August/058906.html
and to the responses to that, especially http://lists.llvm.org/pipermail/cfe-dev/2018-August/059215.html.

Differential Revision: https://reviews.llvm.org/D51680

llvm-svn: 342220
  • Loading branch information
Szelethus committed Sep 14, 2018
1 parent 6cec6c4 commit d6145d9
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 5 deletions.
Expand Up @@ -35,6 +35,13 @@
// `-analyzer-config \
// alpha.cplusplus.UninitializedObject:CheckPointeeInitialization=true`.
//
// - "IgnoreRecordsWithField" (string). If supplied, the checker will not
// analyze structures that have a field with a name or type name that
// matches the given pattern. Defaults to "".
//
// `-analyzer-config \
// alpha.cplusplus.UninitializedObject:IgnoreRecordsWithField="[Tt]ag|[Kk]ind"`.
//
// TODO: With some clever heuristics, some pointers should be dereferenced
// by default. For example, if the pointee is constructed within the
// constructor call, it's reasonable to say that no external object
Expand All @@ -60,7 +67,8 @@ namespace ento {
struct UninitObjCheckerOptions {
bool IsPedantic = false;
bool ShouldConvertNotesToWarnings = false;
bool CheckPointeeInitialization = false;
bool CheckPointeeInitialization = false;
std::string IgnoredRecordsWithFieldPattern;
};

/// A lightweight polymorphic wrapper around FieldRegion *. We'll use this
Expand Down
Expand Up @@ -109,6 +109,10 @@ getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context);
static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
CheckerContext &Context);

/// Checks whether RD contains a field with a name or type name that matches
/// \p Pattern.
static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern);

//===----------------------------------------------------------------------===//
// Methods for UninitializedObjectChecker.
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -228,6 +232,12 @@ bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R,
return true;
}

if (!Opts.IgnoredRecordsWithFieldPattern.empty() &&
shouldIgnoreRecord(RD, Opts.IgnoredRecordsWithFieldPattern)) {
IsAnyFieldInitialized = true;
return false;
}

bool ContainsUninitField = false;

// Are all of this non-union's fields initialized?
Expand Down Expand Up @@ -442,6 +452,19 @@ static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
return false;
}

static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern) {
llvm::Regex R(Pattern);

for (const FieldDecl *FD : RD->fields()) {
if (R.match(FD->getType().getAsString()))
return true;
if (R.match(FD->getName()))
return true;
}

return false;
}

std::string clang::ento::getVariableName(const FieldDecl *Field) {
// If Field is a captured lambda variable, Field->getName() will return with
// an empty string. We can however acquire it's name from the lambda's
Expand Down Expand Up @@ -472,10 +495,13 @@ void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions();
UninitObjCheckerOptions &ChOpts = Chk->Opts;

ChOpts.IsPedantic = AnOpts.getBooleanOption(
"Pedantic", /*DefaultVal*/ false, Chk);
ChOpts.ShouldConvertNotesToWarnings = AnOpts.getBooleanOption(
"NotesAsWarnings", /*DefaultVal*/ false, Chk);
ChOpts.IsPedantic =
AnOpts.getBooleanOption("Pedantic", /*DefaultVal*/ false, Chk);
ChOpts.ShouldConvertNotesToWarnings =
AnOpts.getBooleanOption("NotesAsWarnings", /*DefaultVal*/ false, Chk);
ChOpts.CheckPointeeInitialization = AnOpts.getBooleanOption(
"CheckPointeeInitialization", /*DefaultVal*/ false, Chk);
ChOpts.IgnoredRecordsWithFieldPattern =
AnOpts.getOptionAsString("IgnoreRecordsWithField",
/*DefaultVal*/ "", Chk);
}
136 changes: 136 additions & 0 deletions clang/test/Analysis/cxx-uninitialized-object-unionlike-constructs.cpp
@@ -0,0 +1,136 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.UninitializedObject \
// RUN: -analyzer-config alpha.cplusplus.UninitializedObject:Pedantic=true -DPEDANTIC \
// RUN: -analyzer-config alpha.cplusplus.UninitializedObject:IgnoreRecordsWithField="[Tt]ag|[Kk]ind" \
// RUN: -std=c++11 -verify %s

// expected-no-diagnostics

// Both type and name contains "kind".
struct UnionLikeStruct1 {
enum Kind {
volume,
area
} kind;

int Volume;
int Area;

UnionLikeStruct1(Kind kind, int Val) : kind(kind) {
switch (kind) {
case volume:
Volume = Val;
break;
case area:
Area = Val;
break;
}
}
};

void fUnionLikeStruct1() {
UnionLikeStruct1 t(UnionLikeStruct1::volume, 10);
}

// Only name contains "kind".
struct UnionLikeStruct2 {
enum Type {
volume,
area
} kind;

int Volume;
int Area;

UnionLikeStruct2(Type kind, int Val) : kind(kind) {
switch (kind) {
case volume:
Volume = Val;
break;
case area:
Area = Val;
break;
}
}
};

void fUnionLikeStruct2() {
UnionLikeStruct2 t(UnionLikeStruct2::volume, 10);
}

// Only type contains "kind".
struct UnionLikeStruct3 {
enum Kind {
volume,
area
} type;

int Volume;
int Area;

UnionLikeStruct3(Kind type, int Val) : type(type) {
switch (type) {
case volume:
Volume = Val;
break;
case area:
Area = Val;
break;
}
}
};

void fUnionLikeStruct3() {
UnionLikeStruct3 t(UnionLikeStruct3::volume, 10);
}

// Only type contains "tag".
struct UnionLikeStruct4 {
enum Tag {
volume,
area
} type;

int Volume;
int Area;

UnionLikeStruct4(Tag type, int Val) : type(type) {
switch (type) {
case volume:
Volume = Val;
break;
case area:
Area = Val;
break;
}
}
};

void fUnionLikeStruct4() {
UnionLikeStruct4 t(UnionLikeStruct4::volume, 10);
}

// Both name and type name contains but does not equal to tag/kind.
struct UnionLikeStruct5 {
enum WhateverTagBlahBlah {
volume,
area
} FunnyKindName;

int Volume;
int Area;

UnionLikeStruct5(WhateverTagBlahBlah type, int Val) : FunnyKindName(type) {
switch (FunnyKindName) {
case volume:
Volume = Val;
break;
case area:
Area = Val;
break;
}
}
};

void fUnionLikeStruct5() {
UnionLikeStruct5 t(UnionLikeStruct5::volume, 10);
}
7 changes: 7 additions & 0 deletions clang/www/analyzer/alpha_checks.html
Expand Up @@ -356,6 +356,13 @@ <h3 id="cplusplus_alpha_checkers">C++ Alpha Checkers</h3>
whether the object itself is initialized. Defaults to false. <br>
<code>-analyzer-config alpha.cplusplus.UninitializedObject:CheckPointeeInitialization=true</code>.
</li>
<li>
"<code>IgnoreRecordsWithField</code>" (string). If supplied, the checker will not
analyze structures that have a field with a name or type name that
matches the given pattern. Defaults to <code>""</code>.

<code>-analyzer-config alpha.cplusplus.UninitializedObject:IgnoreRecordsWithField="[Tt]ag|[Kk]ind"</code>.
</li>
</ul></div></div></td>
<td><div class="exampleContainer expandable">
<div class="example"><pre>
Expand Down

0 comments on commit d6145d9

Please sign in to comment.