aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/StaticAnalyzer/Core/AnalyzerOptions.h10
-rw-r--r--lib/StaticAnalyzer/Core/AnalyzerOptions.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp60
-rw-r--r--test/Analysis/analyzer-config.cpp3
-rw-r--r--test/Analysis/diagnostics/explicit-suppression.cpp11
-rw-r--r--test/Analysis/inlining/containers.cpp234
-rw-r--r--test/Analysis/inlining/stl.cpp4
7 files changed, 321 insertions, 8 deletions
diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
index b9f6f6cce1..6dbdbbf89b 100644
--- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
+++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
@@ -198,6 +198,9 @@ private:
/// \sa mayInlineTemplateFunctions
Optional<bool> InlineTemplateFunctions;
+ /// \sa mayInlineCXXContainerCtorsAndDtors
+ Optional<bool> InlineCXXContainerCtorsAndDtors;
+
/// \sa mayInlineObjCMethod
Optional<bool> ObjCInliningMode;
@@ -281,6 +284,13 @@ public:
/// accepts the values "true" and "false".
bool mayInlineTemplateFunctions();
+ /// Returns whether or not constructors and destructors of C++ container
+ /// objects may be considered for inlining.
+ ///
+ /// This is controlled by the 'c++-container-inlining' config option, which
+ /// accepts the values "true" and "false".
+ bool mayInlineCXXContainerCtorsAndDtors();
+
/// Returns whether or not paths that go through null returns should be
/// suppressed.
///
diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
index 465f408e7f..64e1bae8b9 100644
--- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -134,6 +134,13 @@ bool AnalyzerOptions::mayInlineTemplateFunctions() {
/*Default=*/true);
}
+bool AnalyzerOptions::mayInlineCXXContainerCtorsAndDtors() {
+ return getBooleanOption(InlineCXXContainerCtorsAndDtors,
+ "c++-container-inlining",
+ /*Default=*/false);
+}
+
+
bool AnalyzerOptions::mayInlineObjCMethod() {
return getBooleanOption(ObjCInliningMode,
"objc-inlining",
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 67b249c5e9..ee603d728e 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -674,6 +674,53 @@ static CallInlinePolicy mayInlineCallKind(const CallEvent &Call,
return CIP_Allowed;
}
+/// Returns true if the given C++ class is a container.
+///
+/// Our heuristic for this is whether it contains a method named 'begin()' or a
+/// nested type named 'iterator'.
+static bool isContainerClass(const ASTContext &Ctx, const CXXRecordDecl *RD) {
+ // Don't record any path information.
+ CXXBasePaths Paths(false, false, false);
+
+ const IdentifierInfo &BeginII = Ctx.Idents.get("begin");
+ DeclarationName BeginName = Ctx.DeclarationNames.getIdentifier(&BeginII);
+ DeclContext::lookup_const_result BeginDecls = RD->lookup(BeginName);
+ if (!BeginDecls.empty())
+ return true;
+ if (RD->lookupInBases(&CXXRecordDecl::FindOrdinaryMember,
+ BeginName.getAsOpaquePtr(),
+ Paths))
+ return true;
+
+ const IdentifierInfo &IterII = Ctx.Idents.get("iterator");
+ DeclarationName IteratorName = Ctx.DeclarationNames.getIdentifier(&IterII);
+ DeclContext::lookup_const_result IterDecls = RD->lookup(IteratorName);
+ if (!IterDecls.empty())
+ return true;
+ if (RD->lookupInBases(&CXXRecordDecl::FindOrdinaryMember,
+ IteratorName.getAsOpaquePtr(),
+ Paths))
+ return true;
+
+ return false;
+}
+
+/// Returns true if the given function refers to a constructor or destructor of
+/// a C++ container.
+///
+/// We generally do a poor job modeling most containers right now, and would
+/// prefer not to inline their methods.
+static bool isContainerCtorOrDtor(const ASTContext &Ctx,
+ const FunctionDecl *FD) {
+ // Heuristic: a type is a container if it contains a "begin()" method
+ // or a type named "iterator".
+ if (!(isa<CXXConstructorDecl>(FD) || isa<CXXDestructorDecl>(FD)))
+ return false;
+
+ const CXXRecordDecl *RD = cast<CXXMethodDecl>(FD)->getParent();
+ return isContainerClass(Ctx, RD);
+}
+
/// Returns true if the function in \p CalleeADC may be inlined in general.
///
/// This checks static properties of the function, such as its signature and
@@ -681,16 +728,14 @@ static CallInlinePolicy mayInlineCallKind(const CallEvent &Call,
/// in any context.
static bool mayInlineDecl(const CallEvent &Call, AnalysisDeclContext *CalleeADC,
AnalyzerOptions &Opts) {
- const Decl *D = CalleeADC->getDecl();
-
// FIXME: Do not inline variadic calls.
if (Call.isVariadic())
return false;
// Check certain C++-related inlining policies.
- ASTContext &Ctx = D->getASTContext();
+ ASTContext &Ctx = CalleeADC->getASTContext();
if (Ctx.getLangOpts().CPlusPlus) {
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeADC->getDecl())) {
// Conditionally control the inlining of template functions.
if (!Opts.mayInlineTemplateFunctions())
if (FD->getTemplatedKind() != FunctionDecl::TK_NonTemplate)
@@ -701,6 +746,13 @@ static bool mayInlineDecl(const CallEvent &Call, AnalysisDeclContext *CalleeADC,
if (Ctx.getSourceManager().isInSystemHeader(FD->getLocation()))
if (IsInStdNamespace(FD))
return false;
+
+ // Conditionally control the inlining of methods on objects that look
+ // like C++ containers.
+ if (!Opts.mayInlineCXXContainerCtorsAndDtors())
+ if (!Ctx.getSourceManager().isFromMainFile(FD->getLocation()))
+ if (isContainerCtorOrDtor(Ctx, FD))
+ return false;
}
}
diff --git a/test/Analysis/analyzer-config.cpp b/test/Analysis/analyzer-config.cpp
index c3a04c946e..df1d486797 100644
--- a/test/Analysis/analyzer-config.cpp
+++ b/test/Analysis/analyzer-config.cpp
@@ -11,6 +11,7 @@ public:
};
// CHECK: [config]
+// CHECK-NEXT: c++-container-inlining = false
// CHECK-NEXT: c++-inlining = constructors
// CHECK-NEXT: c++-stdlib-inlining = true
// CHECK-NEXT: c++-template-inlining = true
@@ -25,4 +26,4 @@ public:
// CHECK-NEXT: max-times-inline-large = 32
// CHECK-NEXT: mode = deep
// CHECK-NEXT: [stats]
-// CHECK-NEXT: num-entries = 13
+// CHECK-NEXT: num-entries = 14
diff --git a/test/Analysis/diagnostics/explicit-suppression.cpp b/test/Analysis/diagnostics/explicit-suppression.cpp
index 6bc950f5d5..79afeed6c5 100644
--- a/test/Analysis/diagnostics/explicit-suppression.cpp
+++ b/test/Analysis/diagnostics/explicit-suppression.cpp
@@ -13,7 +13,7 @@ void testCopyNull(int *I, int *E) {
std::copy(I, E, (int *)0);
#ifndef SUPPRESSED
// This line number comes from system-header-simulator-cxx.h.
- // expected-warning@65 {{Dereference of null pointer}}
+ // expected-warning@79 {{Dereference of null pointer}}
#endif
}
@@ -66,6 +66,15 @@ void testCopyNull(int *I, int *E) {
+
+
+
+
+
+
+
+
+
// PR15613: expected-* can't refer to diagnostics in other source files.
// The current implementation only matches line numbers, but has an upper limit
// of the number of lines in the main source file.
diff --git a/test/Analysis/inlining/containers.cpp b/test/Analysis/inlining/containers.cpp
new file mode 100644
index 0000000000..4500dff6dc
--- /dev/null
+++ b/test/Analysis/inlining/containers.cpp
@@ -0,0 +1,234 @@
+// RUN: %clang_cc1 -analyze -std=c++11 -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config c++-inlining=destructors -analyzer-config c++-container-inlining=false -verify %s
+// RUN: %clang_cc1 -analyze -std=c++11 -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config c++-inlining=destructors -analyzer-config c++-container-inlining=true -DINLINE=1 -verify %s
+
+#ifndef HEADER
+
+void clang_analyzer_eval(bool);
+void clang_analyzer_checkInlined(bool);
+
+#define HEADER
+#include "containers.cpp"
+#undef HEADER
+
+void test() {
+ MySet set(0);
+
+ clang_analyzer_eval(set.isEmpty());
+#if INLINE
+ // expected-warning@-2 {{TRUE}}
+#else
+ // expected-warning@-4 {{UNKNOWN}}
+#endif
+
+ clang_analyzer_eval(set.raw_begin() == set.raw_end());
+#if INLINE
+ // expected-warning@-2 {{TRUE}}
+#else
+ // expected-warning@-4 {{UNKNOWN}}
+#endif
+
+ clang_analyzer_eval(set.begin().impl == set.end().impl);
+#if INLINE
+ // expected-warning@-2 {{TRUE}}
+#else
+ // expected-warning@-4 {{UNKNOWN}}
+#endif
+}
+
+void testSubclass(MySetSubclass &sub) {
+ sub.useIterator(sub.begin());
+
+ MySetSubclass local;
+}
+
+void testWrappers(BeginOnlySet &w1, IteratorStructOnlySet &w2,
+ IteratorTypedefOnlySet &w3, IteratorUsingOnlySet &w4) {
+ BeginOnlySet local1;
+ IteratorStructOnlySet local2;
+ IteratorTypedefOnlySet local3;
+ IteratorUsingOnlySet local4;
+
+ clang_analyzer_eval(w1.begin().impl.impl == w1.begin().impl.impl);
+#if INLINE
+ // expected-warning@-2 {{TRUE}}
+#else
+ // expected-warning@-4 {{UNKNOWN}}
+#endif
+
+ clang_analyzer_eval(w2.start().impl == w2.start().impl);
+#if INLINE
+ // expected-warning@-2 {{TRUE}}
+#else
+ // expected-warning@-4 {{UNKNOWN}}
+#endif
+
+ clang_analyzer_eval(w3.start().impl == w3.start().impl);
+#if INLINE
+ // expected-warning@-2 {{TRUE}}
+#else
+ // expected-warning@-4 {{UNKNOWN}}
+#endif
+
+ clang_analyzer_eval(w4.start().impl == w4.start().impl);
+#if INLINE
+ // expected-warning@-2 {{TRUE}}
+#else
+ // expected-warning@-4 {{UNKNOWN}}
+#endif
+}
+
+
+#else
+
+class MySet {
+ int *storage;
+ unsigned size;
+public:
+ MySet() : storage(0), size(0) {
+ clang_analyzer_checkInlined(true);
+#if INLINE
+ // expected-warning@-2 {{TRUE}}
+#endif
+ }
+
+ MySet(unsigned n) : storage(new int[n]), size(n) {
+ clang_analyzer_checkInlined(true);
+#if INLINE
+ // expected-warning@-2 {{TRUE}}
+#endif
+ }
+
+ ~MySet() { delete[] storage; }
+
+ bool isEmpty() {
+ clang_analyzer_checkInlined(true); // expected-warning {{TRUE}}
+ return size == 0;
+ }
+
+ struct iterator {
+ int *impl;
+
+ iterator(int *p) : impl(p) {}
+ };
+
+ iterator begin() {
+ clang_analyzer_checkInlined(true); // expected-warning {{TRUE}}
+ return iterator(storage);
+ }
+
+ iterator end() {
+ clang_analyzer_checkInlined(true); // expected-warning {{TRUE}}
+ return iterator(storage+size);
+ }
+
+ typedef int *raw_iterator;
+
+ raw_iterator raw_begin() {
+ clang_analyzer_checkInlined(true); // expected-warning {{TRUE}}
+ return storage;
+ }
+ raw_iterator raw_end() {
+ clang_analyzer_checkInlined(true); // expected-warning {{TRUE}}
+ return storage + size;
+ }
+};
+
+class MySetSubclass : public MySet {
+public:
+ MySetSubclass() {
+ clang_analyzer_checkInlined(true);
+#if INLINE
+ // expected-warning@-2 {{TRUE}}
+#endif
+ }
+
+ void useIterator(iterator i) {
+ clang_analyzer_checkInlined(true); // expected-warning {{TRUE}}
+ }
+};
+
+class BeginOnlySet {
+ MySet impl;
+public:
+ struct IterImpl {
+ MySet::iterator impl;
+ IterImpl(MySet::iterator i) : impl(i) {
+ clang_analyzer_checkInlined(true); // expected-warning {{TRUE}}
+ }
+ };
+
+ BeginOnlySet() {
+ clang_analyzer_checkInlined(true);
+#if INLINE
+ // expected-warning@-2 {{TRUE}}
+#endif
+ }
+
+ typedef IterImpl wrapped_iterator;
+
+ wrapped_iterator begin() {
+ clang_analyzer_checkInlined(true); // expected-warning {{TRUE}}
+ return IterImpl(impl.begin());
+ }
+};
+
+class IteratorTypedefOnlySet {
+ MySet impl;
+public:
+
+ IteratorTypedefOnlySet() {
+ clang_analyzer_checkInlined(true);
+#if INLINE
+ // expected-warning@-2 {{TRUE}}
+#endif
+ }
+
+ typedef MySet::iterator iterator;
+
+ iterator start() {
+ clang_analyzer_checkInlined(true); // expected-warning {{TRUE}}
+ return impl.begin();
+ }
+};
+
+class IteratorUsingOnlySet {
+ MySet impl;
+public:
+
+ IteratorUsingOnlySet() {
+ clang_analyzer_checkInlined(true);
+#if INLINE
+ // expected-warning@-2 {{TRUE}}
+#endif
+ }
+
+ using iterator = MySet::iterator;
+
+ iterator start() {
+ clang_analyzer_checkInlined(true); // expected-warning {{TRUE}}
+ return impl.begin();
+ }
+};
+
+class IteratorStructOnlySet {
+ MySet impl;
+public:
+
+ IteratorStructOnlySet() {
+ clang_analyzer_checkInlined(true);
+#if INLINE
+ // expected-warning@-2 {{TRUE}}
+#endif
+ }
+
+ struct iterator {
+ int *impl;
+ };
+
+ iterator start() {
+ clang_analyzer_checkInlined(true); // expected-warning {{TRUE}}
+ return iterator{impl.begin().impl};
+ }
+};
+
+#endif
diff --git a/test/Analysis/inlining/stl.cpp b/test/Analysis/inlining/stl.cpp
index b09a5126bc..6053daaf3a 100644
--- a/test/Analysis/inlining/stl.cpp
+++ b/test/Analysis/inlining/stl.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config c++-stdlib-inlining=false -verify %s
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config c++-stdlib-inlining=true -DINLINE=1 -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config c++-container-inlining=true -analyzer-config c++-stdlib-inlining=false -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config c++-container-inlining=true -analyzer-config c++-stdlib-inlining=true -DINLINE=1 -verify %s
#include "../Inputs/system-header-simulator-cxx.h"