Added new feature for checking macro and preprocessor conditional consistency.

llvm-svn: 187228
This commit is contained in:
John Thompson 2013-07-26 18:16:22 +00:00
parent 318f09fda7
commit 1e0101461e
6 changed files with 1236 additions and 13 deletions

View File

@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS
add_clang_executable(modularize
Modularize.cpp
PreprocessorTracker.cpp
)
target_link_libraries(modularize

View File

@ -90,10 +90,12 @@
#include <iterator>
#include <string>
#include <vector>
#include "PreprocessorTracker.h"
using namespace clang::tooling;
using namespace clang;
using namespace llvm;
using namespace Modularize;
// Option to specify a file name for a list of header files to check.
cl::opt<std::string>
@ -382,8 +384,14 @@ private:
class CollectEntitiesConsumer : public ASTConsumer {
public:
CollectEntitiesConsumer(EntityMap &Entities, Preprocessor &PP)
: Entities(Entities), PP(PP) {}
CollectEntitiesConsumer(EntityMap &Entities,
PreprocessorTracker &preprocessorTracker,
Preprocessor &PP, StringRef InFile)
: Entities(Entities), PPTracker(preprocessorTracker), PP(PP) {
PPTracker.handlePreprocessorEntry(PP, InFile);
}
~CollectEntitiesConsumer() { PPTracker.handlePreprocessorExit(); }
virtual void HandleTranslationUnit(ASTContext &Ctx) {
SourceManager &SM = Ctx.getSourceManager();
@ -409,33 +417,41 @@ public:
private:
EntityMap &Entities;
PreprocessorTracker &PPTracker;
Preprocessor &PP;
};
class CollectEntitiesAction : public SyntaxOnlyAction {
public:
CollectEntitiesAction(EntityMap &Entities) : Entities(Entities) {}
CollectEntitiesAction(EntityMap &Entities,
PreprocessorTracker &preprocessorTracker)
: Entities(Entities), PPTracker(preprocessorTracker) {}
protected:
virtual clang::ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) {
return new CollectEntitiesConsumer(Entities, CI.getPreprocessor());
return new CollectEntitiesConsumer(Entities, PPTracker,
CI.getPreprocessor(), InFile);
}
private:
EntityMap &Entities;
PreprocessorTracker &PPTracker;
};
class ModularizeFrontendActionFactory : public FrontendActionFactory {
public:
ModularizeFrontendActionFactory(EntityMap &Entities) : Entities(Entities) {}
ModularizeFrontendActionFactory(EntityMap &Entities,
PreprocessorTracker &preprocessorTracker)
: Entities(Entities), PPTracker(preprocessorTracker) {}
virtual CollectEntitiesAction *create() {
return new CollectEntitiesAction(Entities);
return new CollectEntitiesAction(Entities, PPTracker);
}
private:
EntityMap &Entities;
PreprocessorTracker &PPTracker;
};
int main(int argc, const char **argv) {
@ -464,10 +480,14 @@ int main(int argc, const char **argv) {
Compilations.reset(
new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments));
// Create preprocessor tracker, to watch for macro and conditional problems.
OwningPtr<PreprocessorTracker> PPTracker(PreprocessorTracker::create());
// Parse all of the headers, detecting duplicates.
EntityMap Entities;
ClangTool Tool(*Compilations, Headers);
int HadErrors = Tool.run(new ModularizeFrontendActionFactory(Entities));
int HadErrors =
Tool.run(new ModularizeFrontendActionFactory(Entities, *PPTracker));
// Create a place to save duplicate entity locations, separate bins per kind.
typedef SmallVector<Location, 8> LocationArray;
@ -515,6 +535,16 @@ int main(int argc, const char **argv) {
}
}
// Complain about macro instance in header files that differ based on how
// they are included.
if (PPTracker->reportInconsistentMacros(errs()))
HadErrors = 1;
// Complain about preprocessor conditional directives in header files that
// differ based on how they are included.
if (PPTracker->reportInconsistentConditionals(errs()))
HadErrors = 1;
// Complain about any headers that have contents that differ based on how
// they are included.
// FIXME: Could we provide information about which preprocessor conditionals
@ -530,7 +560,7 @@ int main(int argc, const char **argv) {
HadErrors = 1;
errs() << "error: header '" << H->first->getName()
<< "' has different contents depending on how it was included\n";
<< "' has different contents depending on how it was included.\n";
for (unsigned I = 0, N = H->second.size(); I != N; ++I) {
errs() << "note: '" << H->second[I].Name << "' in "
<< H->second[I].Loc.File->getName() << " at "

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,53 @@
//===- PreprocessorTracker.h - Tracks preprocessor activities -*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===--------------------------------------------------------------------===//
///
/// \file
/// \brief Track preprocessor activities for modularize.
///
//===--------------------------------------------------------------------===//
#ifndef MODULARIZE_PREPROCESSOR_TRACKER_H
#define MODULARIZE_PREPROCESSOR_TRACKER_H
#include "clang/Lex/Preprocessor.h"
namespace Modularize {
// Preprocessor tracker for modularize.
//
// This class stores information about all the headers processed in the
// course of running modularize.
class PreprocessorTracker {
public:
virtual ~PreprocessorTracker();
// Handle entering a preprocessing session.
// (Called after a Preprocessor object is created, but before preprocessing.)
virtual void handlePreprocessorEntry(clang::Preprocessor &PP,
llvm::StringRef RootHeaderFile) = 0;
// Handle exiting a preprocessing session.
// (Called after preprocessing is complete, but before the Preprocessor
// object is destroyed.)
virtual void handlePreprocessorExit() = 0;
// Report on inconsistent macro instances.
// Returns true if any mismatches.
virtual bool reportInconsistentMacros(llvm::raw_ostream &OS) = 0;
// Report on inconsistent conditional directive instances.
// Returns true if any mismatches.
virtual bool reportInconsistentConditionals(llvm::raw_ostream &OS) = 0;
// Create instance of PreprocessorTracker.
static PreprocessorTracker *create();
};
} // end namespace Modularize
#endif

View File

@ -1,11 +1,18 @@
// Set up so TypeInt only defined during InconsistentHeader1.h include.
#ifdef SYMBOL1
#define SYMBOL 1
#define FUNC_STYLE(a, b) a || b
#endif
#ifdef SYMBOL2
#define SYMBOL 2
#define FUNC_STYLE(a, b) a &&b
#endif
#if SYMBOL == 1
typedef int TypeInt;
#endif
int var = FUNC_STYLE(1, 0);
#if defined(SYMBOL1)
#endif

View File

@ -4,9 +4,105 @@ Inputs/InconsistentHeader1.h
Inputs/InconsistentHeader2.h
# CHECK: error: macro 'SYMBOL' defined at multiple locations:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:3:9
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:6:9
# CHECK-NEXT: error: header '{{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h' has different contents depending on how it was included
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:3:9
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:7:9
# CHECK-NEXT: error: macro 'FUNC_STYLE' defined at multiple locations:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:4:9
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:8:9
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:15:11:
# CHECK-NEXT: int var = FUNC_STYLE(1, 0);
# CHECK-NEXT: ^
# CHECK-NEXT: error: Macro instance 'FUNC_STYLE(1, 0);' has different values in this header, depending on how it was included.
# CHECK-NEXT: 'FUNC_STYLE(1, 0);' expanded to: '1||0' with respect to these inclusion paths:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:4:9:
# CHECK-NEXT: #define FUNC_STYLE(a, b) a || b
# CHECK-NEXT: ^
# CHECK-NEXT: Macro defined here.
# CHECK-NEXT: 'FUNC_STYLE(1, 0);' expanded to: '1&&0' with respect to these inclusion paths:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:8:9:
# CHECK-NEXT: #define FUNC_STYLE(a, b) a &&b
# CHECK-NEXT: ^
# CHECK-NEXT: Macro defined here.
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:11:5:
# CHECK-NEXT: #if SYMBOL == 1
# CHECK-NEXT: ^
# CHECK-NEXT: error: Macro instance 'SYMBOL' has different values in this header, depending on how it was included.
# CHECK-NEXT: 'SYMBOL' expanded to: '1' with respect to these inclusion paths:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:3:9:
# CHECK-NEXT: #define SYMBOL 1
# CHECK-NEXT: ^
# CHECK-NEXT: Macro defined here.
# CHECK-NEXT: 'SYMBOL' expanded to: '2' with respect to these inclusion paths:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:7:9:
# CHECK-NEXT: #define SYMBOL 2
# CHECK-NEXT: ^
# CHECK-NEXT: Macro defined here.
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:17:5:
# CHECK-NEXT: #if defined(SYMBOL1)
# CHECK-NEXT: ^
# CHECK-NEXT: error: Macro instance 'defined(SYMBOL1)' has different values in this header, depending on how it was included.
# CHECK-NEXT: 'defined(SYMBOL1)' expanded to: 'true' with respect to these inclusion paths:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h:3:9:
# CHECK-NEXT: #define SYMBOL1 1
# CHECK-NEXT: ^
# CHECK-NEXT: Macro defined here.
# CHECK-NEXT: 'defined(SYMBOL1)' expanded to: 'false' with respect to these inclusion paths:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
# CHECK-NEXT: (no macro definition)
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:11:2
# CHECK-NEXT: #if SYMBOL == 1
# CHECK-NEXT: ^
# CHECK-NEXT: error: Conditional expression instance 'SYMBOL == 1' has different values in this header, depending on how it was included.
# CHECK-NEXT: 'SYMBOL == 1' expanded to: 'true' with respect to these inclusion paths:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
# CHECK-NEXT: 'SYMBOL == 1' expanded to: 'false' with respect to these inclusion paths:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:2:2
# CHECK-NEXT: #ifdef SYMBOL1
# CHECK-NEXT: ^
# CHECK-NEXT: error: Conditional expression instance 'SYMBOL1' has different values in this header, depending on how it was included.
# CHECK-NEXT: 'SYMBOL1' expanded to: 'true' with respect to these inclusion paths:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
# CHECK-NEXT: 'SYMBOL1' expanded to: 'false' with respect to these inclusion paths:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:6:2
# CHECK-NEXT: #ifdef SYMBOL2
# CHECK-NEXT: ^
# CHECK-NEXT: error: Conditional expression instance 'SYMBOL2' has different values in this header, depending on how it was included.
# CHECK-NEXT: 'SYMBOL2' expanded to: 'false' with respect to these inclusion paths:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
# CHECK-NEXT: 'SYMBOL2' expanded to: 'true' with respect to these inclusion paths:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:17:2
# CHECK-NEXT: #if defined(SYMBOL1)
# CHECK-NEXT: ^
# CHECK-NEXT: error: Conditional expression instance 'defined(SYMBOL1)' has different values in this header, depending on how it was included.
# CHECK-NEXT: 'defined(SYMBOL1)' expanded to: 'true' with respect to these inclusion paths:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
# CHECK-NEXT: 'defined(SYMBOL1)' expanded to: 'false' with respect to these inclusion paths:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
# CHECK-NEXT: error: header '{{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h' has different contents depending on how it was included.
# CHECK-NEXT: note: 'SYMBOL' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 3:9 not always provided
# CHECK-NEXT: note: 'SYMBOL' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 6:9 not always provided
# CHECK-NEXT: note: 'TypeInt' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 10:13 not always provided
# CHECK-NEXT: note: 'FUNC_STYLE' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 4:9 not always provided
# CHECK-NEXT: note: 'SYMBOL' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 7:9 not always provided
# CHECK-NEXT: note: 'FUNC_STYLE' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 8:9 not always provided
# CHECK-NEXT: note: 'TypeInt' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 12:13 not always provided