From 43cdc8690f87bf974c1f6d2642da75ca6306fbfd Mon Sep 17 00:00:00 2001 From: Kristof Umann Date: Fri, 14 Sep 2018 10:10:09 +0000 Subject: [PATCH] [analyzer][UninitializedObjectChecker] New flag to ignore records based 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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@342220 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../UninitializedObject/UninitializedObject.h | 10 +- .../UninitializedObjectChecker.cpp | 34 ++++- ...nitialized-object-unionlike-constructs.cpp | 136 ++++++++++++++++++ www/analyzer/alpha_checks.html | 7 + 4 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 test/Analysis/cxx-uninitialized-object-unionlike-constructs.cpp diff --git a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h index fbc241a283..e1e8cfa582 100644 --- a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h +++ b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h @@ -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 @@ -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 diff --git a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp index 92e21f9cce..b632229321 100644 --- a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp @@ -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. //===----------------------------------------------------------------------===// @@ -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? @@ -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 @@ -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); } diff --git a/test/Analysis/cxx-uninitialized-object-unionlike-constructs.cpp b/test/Analysis/cxx-uninitialized-object-unionlike-constructs.cpp new file mode 100644 index 0000000000..dc52afd901 --- /dev/null +++ b/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); +} diff --git a/www/analyzer/alpha_checks.html b/www/analyzer/alpha_checks.html index 837ccce624..53635b7050 100644 --- a/www/analyzer/alpha_checks.html +++ b/www/analyzer/alpha_checks.html @@ -356,6 +356,13 @@ It has several options: whether the object itself is initialized. Defaults to false.
-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". +