Skip to content

Commit 9873ef4

Browse files
author
Balazs Benics
committed
[analyzer] Ignore flex generated files
Some projects [1,2,3] have flex-generated files besides bison-generated ones. Unfortunately, the comment `"/* A lexical scanner generated by flex */"` generated by the tools is not necessarily at the beginning of the file, thus we need to quickly skim through the file for this needle string. Luckily, StringRef can do this operation in an efficient way. That being said, now the bison comment is not required to be at the very beginning of the file. This allows us to detect a couple more cases [4,5,6]. Alternatively, we could say that we only allow whitespace characters before matching the bison/flex header comment. That would prevent the (probably) unnecessary string search in the buffer. However, I could not verify that these tools would actually respect this assumption. Additionally to this, e.g. the Twin project [1] has other non-whitespace characters (some preprocessor directives) before the flex-generated header comment. So the heuristic in the previous paragraph won't work with that. Thus, I would advocate the current implementation. According to my measurement, this patch won't introduce measurable performance degradation, even though we will do 2 linear scans. I introduce the ignore-bison-generated-files and ignore-flex-generated-files to disable skipping these files. Both of these options are true by default. [1]: https://github.com/cosmos72/twin/blob/master/server/rcparse_lex.cpp#L7 [2]: https://github.com/marcauresoar/make-examples/blob/22362cdcf9dd7c597b5049ce7f176621e2e9ac7a/sandbox/count-words/lexer.c#L6 [3]: https://github.com/vladcmanea/2nd-faculty-year-Formal-Languages---Automata-assignments/blob/11abdf64629d9eb741438ba69f04636769d5a374/lab1/lex.yy.c#L6 [4]: https://github.com/KritikaChoudhary/System-Software-Lab/blob/47f5b2cfe2a2738fd54eae9f8439817f6a22034e/B_yacc/1/y1.tab.h#L2 [5]: https://github.com/VirtualMonitor/VirtualMonitor/blob/71d1bf9b1e7b392a7bd0c73dc217138dc5865651/src/VBox/Additions/x11/x11include/xorg-server-1.8.0/parser.h#L2 [6]: https://github.com/bspaulding/DrawTest/blob/3f773ceb13de14275429036b9cbc5aa19e29bab9/Framework/OpenEars.framework/Versions/A/Headers/jsgf_parser.h#L2 Reviewed By: xazax.hun Differential Revision: https://reviews.llvm.org/D114510
1 parent e9fb4dc commit 9873ef4

File tree

5 files changed

+67
-29
lines changed

5 files changed

+67
-29
lines changed

clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,18 @@ ANALYZER_OPTION(
336336
"might be modeled by the analyzer to never return NULL.",
337337
false)
338338

339+
ANALYZER_OPTION(
340+
bool, ShouldIgnoreBisonGeneratedFiles, "ignore-bison-generated-files",
341+
"If enabled, any files containing the \"/* A Bison parser, made by\" "
342+
"won't be analyzed.",
343+
true)
344+
345+
ANALYZER_OPTION(
346+
bool, ShouldIgnoreFlexGeneratedFiles, "ignore-flex-generated-files",
347+
"If enabled, any files containing the \"/* A lexical scanner generated by "
348+
"flex\" won't be analyzed.",
349+
true)
350+
339351
//===----------------------------------------------------------------------===//
340352
// Unsigned analyzer options.
341353
//===----------------------------------------------------------------------===//

clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
3636
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
3737
#include "llvm/ADT/PostOrderIterator.h"
38+
#include "llvm/ADT/ScopeExit.h"
3839
#include "llvm/ADT/Statistic.h"
3940
#include "llvm/Support/FileSystem.h"
4041
#include "llvm/Support/Path.h"
@@ -493,13 +494,11 @@ void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) {
493494
}
494495
}
495496

496-
static bool isBisonFile(ASTContext &C) {
497+
static bool fileContainsString(StringRef Substring, ASTContext &C) {
497498
const SourceManager &SM = C.getSourceManager();
498499
FileID FID = SM.getMainFileID();
499500
StringRef Buffer = SM.getBufferOrFake(FID).getBuffer();
500-
if (Buffer.startswith("/* A Bison parser, made by"))
501-
return true;
502-
return false;
501+
return Buffer.contains(Substring);
503502
}
504503

505504
void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) {
@@ -546,38 +545,48 @@ void AnalysisConsumer::reportAnalyzerProgress(StringRef S) {
546545
}
547546

548547
void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
549-
550548
// Don't run the actions if an error has occurred with parsing the file.
551549
DiagnosticsEngine &Diags = PP.getDiagnostics();
552550
if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
553551
return;
554552

555-
if (isBisonFile(C)) {
553+
// Explicitly destroy the PathDiagnosticConsumer. This will flush its output.
554+
// FIXME: This should be replaced with something that doesn't rely on
555+
// side-effects in PathDiagnosticConsumer's destructor. This is required when
556+
// used with option -disable-free.
557+
const auto DiagFlusherScopeExit =
558+
llvm::make_scope_exit([this] { Mgr.reset(); });
559+
560+
if (Opts->ShouldIgnoreBisonGeneratedFiles &&
561+
fileContainsString("/* A Bison parser, made by", C)) {
556562
reportAnalyzerProgress("Skipping bison-generated file\n");
557-
} else if (Opts->DisableAllCheckers) {
563+
return;
564+
}
558565

559-
// Don't analyze if the user explicitly asked for no checks to be performed
560-
// on this file.
566+
if (Opts->ShouldIgnoreFlexGeneratedFiles &&
567+
fileContainsString("/* A lexical scanner generated by flex", C)) {
568+
reportAnalyzerProgress("Skipping flex-generated file\n");
569+
return;
570+
}
571+
572+
// Don't analyze if the user explicitly asked for no checks to be performed
573+
// on this file.
574+
if (Opts->DisableAllCheckers) {
561575
reportAnalyzerProgress("All checks are disabled using a supplied option\n");
562-
} else {
563-
// Otherwise, just run the analysis.
564-
runAnalysisOnTranslationUnit(C);
576+
return;
565577
}
566578

579+
// Otherwise, just run the analysis.
580+
runAnalysisOnTranslationUnit(C);
581+
567582
// Count how many basic blocks we have not covered.
568583
NumBlocksInAnalyzedFunctions = FunctionSummaries.getTotalNumBasicBlocks();
569584
NumVisitedBlocksInAnalyzedFunctions =
570585
FunctionSummaries.getTotalNumVisitedBasicBlocks();
571586
if (NumBlocksInAnalyzedFunctions > 0)
572587
PercentReachableBlocks =
573-
(FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) /
588+
(FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) /
574589
NumBlocksInAnalyzedFunctions;
575-
576-
// Explicitly destroy the PathDiagnosticConsumer. This will flush its output.
577-
// FIXME: This should be replaced with something that doesn't rely on
578-
// side-effects in PathDiagnosticConsumer's destructor. This is required when
579-
// used with option -disable-free.
580-
Mgr.reset();
581590
}
582591

583592
AnalysisConsumer::AnalysisMode

clang/test/Analysis/analyzer-config.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@
8383
// CHECK-NEXT: exploration_strategy = unexplored_first_queue
8484
// CHECK-NEXT: faux-bodies = true
8585
// CHECK-NEXT: graph-trim-interval = 1000
86+
// CHECK-NEXT: ignore-bison-generated-files = true
87+
// CHECK-NEXT: ignore-flex-generated-files = true
8688
// CHECK-NEXT: inline-lambdas = true
8789
// CHECK-NEXT: ipa = dynamic-bifurcate
8890
// CHECK-NEXT: ipa-always-inline-size = 3

clang/test/Analysis/flexignore.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify -DEXPECT_NO_DIAGNOSTICS %s
2+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify=conditional %s \
3+
// RUN: -analyzer-config ignore-flex-generated-files=false
4+
5+
#ifdef EXPECT_NO_DIAGNOSTICS
6+
// expected-no-diagnostics
7+
#endif
8+
9+
/* A lexical scanner generated by flex */
10+
11+
void clang_analyzer_warnIfReached();
12+
void foo() {
13+
clang_analyzer_warnIfReached(); // conditional-warning {{REACHABLE}}
14+
}

clang/test/Analysis/yaccignore.c

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
/* A Bison parser, made by GNU Bison 1.875. */
2-
3-
// RUN: rm -rf %t.plist
4-
// RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-output=plist -o %t.plist -verify %s
5-
// RUN: FileCheck --input-file=%t.plist %s
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify -DEXPECT_NO_DIAGNOSTICS %s
2+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify=conditional %s \
3+
// RUN: -analyzer-config ignore-bison-generated-files=false
64

5+
#ifdef EXPECT_NO_DIAGNOSTICS
76
// expected-no-diagnostics
8-
int foo() {
9-
int *x = 0;
10-
return *x; // no-warning
11-
}
7+
#endif
128

13-
// CHECK: <key>diagnostics</key>
9+
/* A Bison parser, made by GNU Bison 1.875. */
10+
11+
void clang_analyzer_warnIfReached();
12+
void foo() {
13+
clang_analyzer_warnIfReached(); // conditional-warning {{REACHABLE}}
14+
}

0 commit comments

Comments
 (0)