[cfe-dev] Clang static analyzer: ignore library headers?
Aemon Cannon
aemoncannon at gmail.com
Thu Oct 3 19:07:39 PDT 2013
I have a work-in-progress patch I'd like to share. Feedback appreciated.
Goal:
I want to be able to analyze the code in my headers while ignoring code
from third-party headers that are included in my sources. Adds a new
analyzer option, analyzer-ignore-headers, for explicitly blacklisting
headers by prefix.
TODO:
* Silence warnings from code in blacklisted headers when that code is
inlined into sources.
* Check case-sensitivity of the local filesystem and choose string Compare
implementation accordingly.
Misc:
* I decided against the -isystem option because I wanted something more
targeted that doesn't require modifying the build command.
* I'm not doing any path expansion or normalization, just a dumb string
comparison. Is this sufficient?
Thanks!
diff --git a/include/clang/Driver/CC1Options.td
b/include/clang/Driver/CC1Options.td
index 9acbd48..cc64c34 100644
--- a/include/clang/Driver/CC1Options.td
+++ b/include/clang/Driver/CC1Options.td
@@ -111,6 +111,9 @@ def analyzer_checker_help : Flag<["-"],
"analyzer-checker-help">,
def analyzer_config : Separate<["-"], "analyzer-config">,
HelpText<"Choose analyzer options to enable">;
+def analyzer_ignore_headers : Separate<["-"], "analyzer-ignore-headers">,
+ HelpText<"Ignore headers by prefix.">;
+
//===----------------------------------------------------------------------===//
// Migrator Options
//===----------------------------------------------------------------------===//
diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
index 618782e..45e75d1 100644
--- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
+++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
@@ -122,6 +122,7 @@ public:
/// \brief Pair of checker name and enable/disable.
std::vector<std::pair<std::string, bool> > CheckersControlList;
+ std::vector<std::string> HeaderBlacklist;
/// \brief A key-value table of use-specified configuration values.
ConfigTable Config;
@@ -267,6 +268,8 @@ public:
/// \sa CXXMemberInliningMode
bool mayInlineCXXMemberFunction(CXXInlineableMemberKind K);
+ bool headerIsBlacklisted(StringRef IncludePath);
+
/// Returns true if ObjectiveC inlining is enabled, false otherwise.
bool mayInlineObjCMethod();
diff --git a/lib/Frontend/CompilerInvocation.cpp
b/lib/Frontend/CompilerInvocation.cpp
index b0d7a0a..6ed2abc 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -280,6 +280,28 @@ static bool ParseAnalyzerArgs(AnalyzerOptions &Opts,
ArgList &Args,
}
}
+ // Go through the analyzer header blacklist.
+ for (arg_iterator it = Args.filtered_begin(OPT_analyzer_ignore_headers),
+ ie = Args.filtered_end(); it != ie; ++it) {
+ const Arg *A = *it;
+ A->claim();
+ StringRef headerList = A->getValue();
+ SmallVector<StringRef, 4> headerVals;
+ headerList.split(headerVals, ",");
+ for (unsigned i = 0, e = headerVals.size(); i != e; ++i) {
+ StringRef val = headerVals[i];
+ if (val.empty()) {
+ // TODO(aemon): properly report problem
+ // Diags.Report(SourceLocation(),
+ // diag::err_analyzer_header_no_value) <<
headerVals[i];
+ Success = false;
+ break;
+ }
+ Opts.HeaderBlacklist.push_back(val.str());
+ }
+ }
+ std::sort(Opts.HeaderBlacklist.begin(), Opts.HeaderBlacklist.end());
+
return Success;
}
diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt
b/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 392995e..123e84f 100644
--- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -50,6 +50,8 @@ add_clang_library(clangStaticAnalyzerCheckers
ObjCMissingSuperCallChecker.cpp
ObjCSelfInitChecker.cpp
ObjCUnusedIVarsChecker.cpp
+ OwnershipChecker.cpp
+ TracingChecker.cpp
PointerArithChecker.cpp
PointerSubChecker.cpp
PthreadLockChecker.cpp
diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
index 9dcf58b..3cfde27 100644
--- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -96,6 +96,16 @@
AnalyzerOptions::mayInlineCXXMemberFunction(CXXInlineableMemberKind K) {
return CXXMemberInliningMode >= K;
}
+bool AnalyzerOptions::headerIsBlacklisted(StringRef IncludePath) {
+ if (HeaderBlacklist.size() == 0) return false;
+ std::vector<std::string>::const_iterator SI =
+ lower_bound(HeaderBlacklist.begin(), HeaderBlacklist.end(),
+ IncludePath);
+ if (SI != HeaderBlacklist.end() && *SI == IncludePath) return true;
+ if (SI == HeaderBlacklist.begin()) return false;
+ return IncludePath.startswith(*(--SI));
+}
+
static StringRef toString(bool b) { return b ? "true" : "false"; }
bool AnalyzerOptions::getBooleanOption(StringRef Name, bool DefaultVal) {
diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index 9efe997..0a13906 100644
--- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -414,13 +414,23 @@ void
AnalysisConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
}
void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) {
+ SourceManager &SM = Ctx->getSourceManager();
for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) {
-
// Skip ObjCMethodDecl, wait for the objc container to avoid
// analyzing twice.
if (isa<ObjCMethodDecl>(*I))
continue;
+ SourceLocation SL = (*I)->getLocation();
+ if (!SM.isInMainFile(SL) && SL.isFileID()) {
+ if (const char* IncludePath = SM.getBufferName(SL)) {
+ StringRef S(IncludePath);
+ if (Opts->headerIsBlacklisted(S)) {
+ continue;
+ }
+ }
+ }
+
LocalTUDecls.push_back(*I);
}
}
diff --git a/unittests/Frontend/AnalyzerOptionsTest.cpp
b/unittests/Frontend/AnalyzerOptionsTest.cpp
new file mode 100644
index 0000000..489772c
--- /dev/null
+++ b/unittests/Frontend/AnalyzerOptionsTest.cpp
@@ -0,0 +1,57 @@
+//===- unittests/Frontend/AnalyzerOptionsTest.cpp - AnalyzerOptions tests
---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace {
+
+TEST(AnalyzerOptionsTest, EmptyBlacklist) {
+ AnalyzerOptions options;
+ ASSERT_FALSE(options.headerIsBlacklisted("C:\\abc\\hello.h"));
+ ASSERT_FALSE(options.headerIsBlacklisted("bla.h"));
+}
+
+TEST(AnalyzerOptionsTest, Simple) {
+ AnalyzerOptions options;
+ options.HeaderBlacklist.push_back("C:\\abc\\");
+ options.HeaderBlacklist.push_back("/system/include");
+ std::sort(options.HeaderBlacklist.begin(),
options.HeaderBlacklist.end());
+ ASSERT_TRUE(options.headerIsBlacklisted("C:\\abc\\hello.h"));
+ ASSERT_FALSE(options.headerIsBlacklisted("/system/hello.h"));
+}
+
+TEST(AnalyzerOptionsTest, LongerList) {
+ AnalyzerOptions options;
+ options.HeaderBlacklist.push_back("D:\\abc\\");
+ options.HeaderBlacklist.push_back("C:\\abc\\");
+ options.HeaderBlacklist.push_back("/var/log/system");
+ options.HeaderBlacklist.push_back("/system/include");
+ options.HeaderBlacklist.push_back("/home/tmp/");
+ options.HeaderBlacklist.push_back("../things/");
+ std::sort(options.HeaderBlacklist.begin(),
options.HeaderBlacklist.end());
+ ASSERT_TRUE(options.headerIsBlacklisted("C:\\abc\\hello.h"));
+ ASSERT_TRUE(options.headerIsBlacklisted("/var/log/system/../hello"));
+ ASSERT_TRUE(options.headerIsBlacklisted("../things/foo.c"));
+ ASSERT_FALSE(options.headerIsBlacklisted("D:\\ab\\"));
+}
+
+TEST(AnalyzerOptionsTest, CaseSensitive) {
+ AnalyzerOptions options;
+ options.HeaderBlacklist.push_back("C:\\abc\\");
+ options.HeaderBlacklist.push_back("/system/include");
+ std::sort(options.HeaderBlacklist.begin(),
options.HeaderBlacklist.end());
+ ASSERT_FALSE(options.headerIsBlacklisted("c:\\abc\\hello.h"));
+ ASSERT_FALSE(options.headerIsBlacklisted("/system/Include/hello.h"));
+}
+
+} // anonymous namespace
diff --git a/unittests/Frontend/CMakeLists.txt
b/unittests/Frontend/CMakeLists.txt
index c65a163..39b4064 100644
--- a/unittests/Frontend/CMakeLists.txt
+++ b/unittests/Frontend/CMakeLists.txt
@@ -8,6 +8,7 @@ set(LLVM_LINK_COMPONENTS
add_clang_unittest(FrontendTests
FrontendActionTest.cpp
+ AnalyzerOptionsTest.cpp
)
target_link_libraries(FrontendTests
clangFrontend
On Tue, Sep 17, 2013 at 12:14 PM, Jordan Rose <jordan_rose at apple.com> wrote:
>
> On Sep 16, 2013, at 22:34 , Ben Pope <benpope81 at gmail.com> wrote:
>
> On 17/09/13 01:16, Anna Zaks wrote:
>
> We don't have a list of known third-party headers anywhere, but having
> this seems generally useful.
>
>
> Determining them based on -I vs -isystem would seem to be a sensible
> distinction.
>
> At least, that's what I use to include third party libraries when I want
> to ignore warnings they produce. It's entirely possible I'm abusing
> that feature.
>
>
> It's a bit of an abuse but likely not a harmful one. Huh, we don't
> actually have that mode now. Regular mode runs path-sensitive checks on the
> main source file and non-path-sensitive checks on the main source file +
> non-system headers. -analyzer-opt-analyze-headers mode runs path-sensitive
> checks everywhere. Patches welcome?
>
> As for an explicit blacklist, we've gotten that request before, but only
> for main source files: PR2706 <http://llvm.org/bugs/show_bug.cgi?id=2706>
> and <rdar://problem/9063762>. A header file filter would be slightly
> different.
>
> Jordan
>
> _______________________________________________
> cfe-dev mailing list
> cfe-dev at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20131003/4b8e444f/attachment.html>
More information about the cfe-dev
mailing list