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 add_clang_executable(modularize
Modularize.cpp Modularize.cpp
PreprocessorTracker.cpp
) )
target_link_libraries(modularize target_link_libraries(modularize

View File

@ -90,10 +90,12 @@
#include <iterator> #include <iterator>
#include <string> #include <string>
#include <vector> #include <vector>
#include "PreprocessorTracker.h"
using namespace clang::tooling; using namespace clang::tooling;
using namespace clang; using namespace clang;
using namespace llvm; using namespace llvm;
using namespace Modularize;
// Option to specify a file name for a list of header files to check. // Option to specify a file name for a list of header files to check.
cl::opt<std::string> cl::opt<std::string>
@ -382,8 +384,14 @@ private:
class CollectEntitiesConsumer : public ASTConsumer { class CollectEntitiesConsumer : public ASTConsumer {
public: public:
CollectEntitiesConsumer(EntityMap &Entities, Preprocessor &PP) CollectEntitiesConsumer(EntityMap &Entities,
: Entities(Entities), PP(PP) {} PreprocessorTracker &preprocessorTracker,
Preprocessor &PP, StringRef InFile)
: Entities(Entities), PPTracker(preprocessorTracker), PP(PP) {
PPTracker.handlePreprocessorEntry(PP, InFile);
}
~CollectEntitiesConsumer() { PPTracker.handlePreprocessorExit(); }
virtual void HandleTranslationUnit(ASTContext &Ctx) { virtual void HandleTranslationUnit(ASTContext &Ctx) {
SourceManager &SM = Ctx.getSourceManager(); SourceManager &SM = Ctx.getSourceManager();
@ -409,33 +417,41 @@ public:
private: private:
EntityMap &Entities; EntityMap &Entities;
PreprocessorTracker &PPTracker;
Preprocessor &PP; Preprocessor &PP;
}; };
class CollectEntitiesAction : public SyntaxOnlyAction { class CollectEntitiesAction : public SyntaxOnlyAction {
public: public:
CollectEntitiesAction(EntityMap &Entities) : Entities(Entities) {} CollectEntitiesAction(EntityMap &Entities,
PreprocessorTracker &preprocessorTracker)
: Entities(Entities), PPTracker(preprocessorTracker) {}
protected: protected:
virtual clang::ASTConsumer *CreateASTConsumer(CompilerInstance &CI, virtual clang::ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) { StringRef InFile) {
return new CollectEntitiesConsumer(Entities, CI.getPreprocessor()); return new CollectEntitiesConsumer(Entities, PPTracker,
CI.getPreprocessor(), InFile);
} }
private: private:
EntityMap &Entities; EntityMap &Entities;
PreprocessorTracker &PPTracker;
}; };
class ModularizeFrontendActionFactory : public FrontendActionFactory { class ModularizeFrontendActionFactory : public FrontendActionFactory {
public: public:
ModularizeFrontendActionFactory(EntityMap &Entities) : Entities(Entities) {} ModularizeFrontendActionFactory(EntityMap &Entities,
PreprocessorTracker &preprocessorTracker)
: Entities(Entities), PPTracker(preprocessorTracker) {}
virtual CollectEntitiesAction *create() { virtual CollectEntitiesAction *create() {
return new CollectEntitiesAction(Entities); return new CollectEntitiesAction(Entities, PPTracker);
} }
private: private:
EntityMap &Entities; EntityMap &Entities;
PreprocessorTracker &PPTracker;
}; };
int main(int argc, const char **argv) { int main(int argc, const char **argv) {
@ -464,10 +480,14 @@ int main(int argc, const char **argv) {
Compilations.reset( Compilations.reset(
new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments)); 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. // Parse all of the headers, detecting duplicates.
EntityMap Entities; EntityMap Entities;
ClangTool Tool(*Compilations, Headers); 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. // Create a place to save duplicate entity locations, separate bins per kind.
typedef SmallVector<Location, 8> LocationArray; 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 // Complain about any headers that have contents that differ based on how
// they are included. // they are included.
// FIXME: Could we provide information about which preprocessor conditionals // FIXME: Could we provide information about which preprocessor conditionals
@ -530,7 +560,7 @@ int main(int argc, const char **argv) {
HadErrors = 1; HadErrors = 1;
errs() << "error: header '" << H->first->getName() 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) { for (unsigned I = 0, N = H->second.size(); I != N; ++I) {
errs() << "note: '" << H->second[I].Name << "' in " errs() << "note: '" << H->second[I].Name << "' in "
<< H->second[I].Loc.File->getName() << " at " << 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. // Set up so TypeInt only defined during InconsistentHeader1.h include.
#ifdef SYMBOL1 #ifdef SYMBOL1
#define SYMBOL 1 #define SYMBOL 1
#define FUNC_STYLE(a, b) a || b
#endif #endif
#ifdef SYMBOL2 #ifdef SYMBOL2
#define SYMBOL 2 #define SYMBOL 2
#define FUNC_STYLE(a, b) a &&b
#endif #endif
#if SYMBOL == 1 #if SYMBOL == 1
typedef int TypeInt; typedef int TypeInt;
#endif #endif
int var = FUNC_STYLE(1, 0);
#if defined(SYMBOL1)
#endif

View File

@ -5,8 +5,104 @@ Inputs/InconsistentHeader2.h
# CHECK: error: macro 'SYMBOL' defined at multiple locations: # CHECK: error: macro 'SYMBOL' defined at multiple locations:
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:3:9 # CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:3:9
# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:6:9 # CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:7:9
# CHECK-NEXT: error: header '{{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h' has different contents depending on how it was included # 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 3:9 not always provided
# CHECK-NEXT: note: 'SYMBOL' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 6:9 not always provided # CHECK-NEXT: note: 'FUNC_STYLE' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 4:9 not always provided
# CHECK-NEXT: note: 'TypeInt' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 10:13 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