Skip to content

Commit ad4c801

Browse files
kyleheadleyjohn-h-kastnermattmccutchen-cci
authored
One clang pass (#488)
* prototype without Diagnostics * enableSourceFileDiagnostics * only enable diags once per ast * add fail case * FileEntry path before CanonicalFilePath * Diagnostic errors cause non-zero exit code * setup multipass verifier * remove unused prefixes * delete unused code; create ASTs in seperate function * rework canonical file path * add test refactoring TODOs * :disable diag_verifier tests * Wording Co-authored-by: John Kastner <[email protected]> * Slight improvements and comments for file path canonicalization (for possible addition to #488) (#508) * Slight improvements and comments for file path canonicalization. In particular, getCanonicalFilePath (the version that asserts) is no longer used. * Add comments about blocks around diagnostic generation. Co-authored-by: John Kastner <[email protected]> Co-authored-by: Matt McCutchen (Correct Computation) <[email protected]>
1 parent a381b70 commit ad4c801

16 files changed

+196
-201
lines changed

clang/include/clang/3C/3C.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,10 @@ class _3CInterface {
104104
const std::vector<std::string> &SourceFileList,
105105
clang::tooling::CompilationDatabase *CompDB);
106106

107-
// Constraint Building.
107+
// Call clang to provide the data
108+
bool parseASTs();
109+
110+
// Constraints
108111

109112
// Create ConstraintVariables to hold constraints
110113
bool addVariables();
@@ -134,14 +137,15 @@ class _3CInterface {
134137
// Write all converted versions of the files in the source file list
135138
// to disk
136139
bool writeAllConvertedFilesToDisk();
137-
// Write the current converted state of the provided file.
138-
bool writeConvertedFileToDisk(const std::string &FilePath);
139140

140141
private:
141142
_3CInterface(const struct _3COptions &CCopt,
142143
const std::vector<std::string> &SourceFileList,
143144
clang::tooling::CompilationDatabase *CompDB, bool &Failed);
144145

146+
// saved ASTs
147+
std::vector< std::unique_ptr< ASTUnit >> ASTs;
148+
145149
// Are constraints already built?
146150
bool ConstraintsBuilt;
147151
void invalidateAllConstraintsWithReason(Constraint *ConstraintToRemove);

clang/include/clang/3C/Utils.h

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -104,18 +104,12 @@ bool hasFunctionBody(clang::Decl *D);
104104

105105
std::string getStorageQualifierString(clang::Decl *D);
106106

107-
// Use this version for user input that has not yet been validated.
108107
std::error_code tryGetCanonicalFilePath(const std::string &FileName,
109108
std::string &AbsoluteFp);
110109

111-
// This version asserts success. It may be used for convenience for files we are
112-
// already accessing and thus believe should exist, modulo race conditions and
113-
// the like.
114-
void getCanonicalFilePath(const std::string &FileName, std::string &AbsoluteFp);
115-
116110
// This compares entire path components: it's smart enough to know that "foo.c"
117111
// does not start with "foo". It's not smart about anything else, so you should
118-
// probably put both paths through getCanonicalFilePath first.
112+
// probably put both paths through tryGetCanonicalFilePath first.
119113
bool filePathStartsWith(const std::string &Path, const std::string &Prefix);
120114

121115
bool isNULLExpression(clang::Expr *E, clang::ASTContext &C);
@@ -169,8 +163,8 @@ bool isCastSafe(clang::QualType DstType, clang::QualType SrcType);
169163
// Check if the provided file path belongs to the input project
170164
// and can be rewritten.
171165
//
172-
// For accurate results, the path must have gone through getCanonicalFilePath.
173-
// Note that the file name of a PersistentSourceLoc is always canonical.
166+
// For accurate results, the path must be canonical. The file name of a
167+
// PersistentSourceLoc can normally be assumed to be canonical.
174168
bool canWrite(const std::string &FilePath);
175169

176170
// Check if the provided variable has void as one of its type.

clang/lib/3C/3C.cpp

Lines changed: 76 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -65,78 +65,9 @@ bool RemoveItypes;
6565
bool ForceItypes;
6666
#endif
6767

68-
static ClangTool *GlobalCTool = nullptr;
69-
7068
static CompilationDatabase *CurrCompDB = nullptr;
7169
static tooling::CommandLineArguments SourceFiles;
7270

73-
template <typename T, typename V>
74-
class GenericAction : public ASTFrontendAction {
75-
public:
76-
GenericAction(V &I) : Info(I) {}
77-
78-
virtual std::unique_ptr<ASTConsumer>
79-
CreateASTConsumer(CompilerInstance &Compiler, StringRef InFile) {
80-
return std::unique_ptr<ASTConsumer>(new T(Info, &Compiler.getASTContext()));
81-
}
82-
83-
private:
84-
V &Info;
85-
};
86-
87-
template <typename T, typename V>
88-
class RewriteAction : public ASTFrontendAction {
89-
public:
90-
RewriteAction(V &I) : Info(I) {}
91-
92-
virtual std::unique_ptr<ASTConsumer>
93-
CreateASTConsumer(CompilerInstance &Compiler, StringRef InFile) {
94-
return std::unique_ptr<ASTConsumer>(new T(Info));
95-
}
96-
97-
private:
98-
V &Info;
99-
};
100-
101-
template <typename T>
102-
std::unique_ptr<FrontendActionFactory>
103-
newFrontendActionFactoryA(ProgramInfo &I, bool VerifyTheseDiagnostics = false) {
104-
class ArgFrontendActionFactory : public FrontendActionFactory {
105-
public:
106-
explicit ArgFrontendActionFactory(ProgramInfo &I,
107-
bool VerifyTheseDiagnostics)
108-
: Info(I), VerifyTheseDiagnostics(VerifyTheseDiagnostics) {}
109-
110-
std::unique_ptr<FrontendAction> create() override {
111-
return std::unique_ptr<FrontendAction>(new T(Info));
112-
}
113-
114-
bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
115-
FileManager *Files,
116-
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
117-
DiagnosticConsumer *DiagConsumer) override {
118-
if (VerifyTheseDiagnostics) {
119-
// Mirroring the logic of clang::ParseDiagnosticArgs in
120-
// clang/lib/Frontend/CompilerInvocation.cpp. In particular, note that
121-
// VerifyPrefixes is assumed to be sorted, in case we add more in the
122-
// future.
123-
DiagnosticOptions &DiagOpts = Invocation->getDiagnosticOpts();
124-
DiagOpts.VerifyDiagnostics = true;
125-
DiagOpts.VerifyPrefixes.push_back("expected");
126-
}
127-
return FrontendActionFactory::runInvocation(
128-
Invocation, Files, PCHContainerOps, DiagConsumer);
129-
}
130-
131-
private:
132-
ProgramInfo &Info;
133-
bool VerifyTheseDiagnostics;
134-
};
135-
136-
return std::unique_ptr<FrontendActionFactory>(
137-
new ArgFrontendActionFactory(I, VerifyTheseDiagnostics));
138-
}
139-
14071
ArgumentsAdjuster getIgnoreCheckedPointerAdjuster() {
14172
return [](const CommandLineArguments &Args, StringRef /*unused*/) {
14273
CommandLineArguments AdjustedArgs;
@@ -154,13 +85,16 @@ ArgumentsAdjuster getIgnoreCheckedPointerAdjuster() {
15485
return AdjustedArgs;
15586
};
15687
}
157-
158-
static ClangTool &getGlobalClangTool() {
159-
if (GlobalCTool == nullptr) {
160-
GlobalCTool = new ClangTool(*CurrCompDB, SourceFiles);
161-
GlobalCTool->appendArgumentsAdjuster(getIgnoreCheckedPointerAdjuster());
162-
}
163-
return *GlobalCTool;
88+
ArgumentsAdjuster addVerifyAdjuster() {
89+
return [](const CommandLineArguments &Args, StringRef /*unused*/) {
90+
CommandLineArguments AdjustedArgs(Args);
91+
if (std::find(AdjustedArgs.begin(),AdjustedArgs.end(),"-verify")
92+
== AdjustedArgs.end()) {
93+
AdjustedArgs.push_back("-Xclang");
94+
AdjustedArgs.push_back("-verify");
95+
}
96+
return AdjustedArgs;
97+
};
16498
}
16599

166100
void dumpConstraintOutputJson(const std::string &PostfixStr,
@@ -347,42 +281,53 @@ _3CInterface::_3CInterface(const struct _3COptions &CCopt,
347281
GlobalProgramInfo.getPerfStats().startTotalTime();
348282
}
349283

350-
bool _3CInterface::addVariables() {
284+
bool _3CInterface::parseASTs() {
351285

352286
std::lock_guard<std::mutex> Lock(InterfaceMutex);
353287

354-
ClangTool &Tool = getGlobalClangTool();
288+
auto *Tool = new ClangTool(*CurrCompDB, SourceFiles);
289+
Tool->appendArgumentsAdjuster(getIgnoreCheckedPointerAdjuster());
290+
// TODO: This currently only enables compiler diagnostic verification.
291+
// see https://github.com/correctcomputation/checkedc-clang/issues/425
292+
// for status.
293+
if (VerifyDiagnosticOutput)
294+
Tool->appendArgumentsAdjuster(addVerifyAdjuster());
355295

356-
// 1a. Add Variables.
357-
std::unique_ptr<ToolAction> AdderTool = newFrontendActionFactoryA<
358-
GenericAction<VariableAdderConsumer, ProgramInfo>>(GlobalProgramInfo);
296+
// load the ASTs
297+
return !Tool->buildASTs(ASTs);
298+
}
359299

360-
if (AdderTool) {
361-
int ToolExitCode = Tool.run(AdderTool.get());
362-
if (ToolExitCode != 0)
363-
return false;
364-
} else
365-
llvm_unreachable("No action");
300+
bool _3CInterface::addVariables() {
366301

367-
return true;
302+
std::lock_guard<std::mutex> Lock(InterfaceMutex);
303+
304+
// 1. Add Variables.
305+
VariableAdderConsumer VA = VariableAdderConsumer(GlobalProgramInfo, nullptr);
306+
unsigned int Errs = 0;
307+
for (auto &TU : ASTs) {
308+
TU->enableSourceFileDiagnostics();
309+
VA.HandleTranslationUnit(TU->getASTContext());
310+
TU->getDiagnostics().getClient()->EndSourceFile();
311+
Errs += TU->getDiagnostics().getClient()->getNumErrors();
312+
}
313+
314+
return Errs == 0;
368315
}
369316

370317
bool _3CInterface::buildInitialConstraints() {
371318

372319
std::lock_guard<std::mutex> Lock(InterfaceMutex);
373320

374-
ClangTool &Tool = getGlobalClangTool();
375-
376-
// 1b. Gather constraints.
377-
std::unique_ptr<ToolAction> ConstraintTool = newFrontendActionFactoryA<
378-
GenericAction<ConstraintBuilderConsumer, ProgramInfo>>(GlobalProgramInfo);
379-
380-
if (ConstraintTool) {
381-
int ToolExitCode = Tool.run(ConstraintTool.get());
382-
if (ToolExitCode != 0)
383-
return false;
384-
} else
385-
llvm_unreachable("No action");
321+
// 2. Gather constraints.
322+
ConstraintBuilderConsumer CB = ConstraintBuilderConsumer(GlobalProgramInfo, nullptr);
323+
unsigned int Errs = 0;
324+
for (auto &TU : ASTs) {
325+
TU->enableSourceFileDiagnostics();
326+
CB.HandleTranslationUnit(TU->getASTContext());
327+
TU->getDiagnostics().getClient()->EndSourceFile();
328+
Errs += TU->getDiagnostics().getClient()->getNumErrors();
329+
}
330+
if (Errs > 0) return false;
386331

387332
if (!GlobalProgramInfo.link()) {
388333
errs() << "Linking failed!\n";
@@ -398,7 +343,7 @@ bool _3CInterface::solveConstraints() {
398343
std::lock_guard<std::mutex> Lock(InterfaceMutex);
399344
assert(ConstraintsBuilt && "Constraints not yet built. We need to call "
400345
"build constraint before trying to solve them.");
401-
// 2. Solve constraints.
346+
// 3. Solve constraints.
402347
if (Verbose)
403348
errs() << "Solving constraints\n";
404349

@@ -420,7 +365,6 @@ bool _3CInterface::solveConstraints() {
420365
if (DumpIntermediate)
421366
dumpConstraintOutputJson(FINAL_OUTPUT_SUFFIX, GlobalProgramInfo);
422367

423-
ClangTool &Tool = getGlobalClangTool();
424368
if (AllTypes) {
425369
if (DebugArrSolver)
426370
GlobalProgramInfo.getABoundsInfo().dumpAVarGraph(
@@ -430,31 +374,32 @@ bool _3CInterface::solveConstraints() {
430374
// bounds declarations.
431375
GlobalProgramInfo.getABoundsInfo().performFlowAnalysis(&GlobalProgramInfo);
432376

433-
// 3. Infer the bounds based on calls to malloc and calloc
434-
std::unique_ptr<ToolAction> ABInfTool = newFrontendActionFactoryA<
435-
GenericAction<AllocBasedBoundsInference, ProgramInfo>>(
436-
GlobalProgramInfo);
437-
if (ABInfTool) {
438-
int ToolExitCode = Tool.run(ABInfTool.get());
439-
if (ToolExitCode != 0)
440-
return false;
441-
} else
442-
llvm_unreachable("No Action");
377+
// 4. Infer the bounds based on calls to malloc and calloc
378+
AllocBasedBoundsInference ABBI = AllocBasedBoundsInference(GlobalProgramInfo, nullptr);
379+
unsigned int Errs = 0;
380+
for (auto &TU : ASTs) {
381+
TU->enableSourceFileDiagnostics();
382+
ABBI.HandleTranslationUnit(TU->getASTContext());
383+
TU->getDiagnostics().getClient()->EndSourceFile();
384+
Errs += TU->getDiagnostics().getClient()->getNumErrors();
385+
}
386+
if (Errs > 0) return false;
443387

444388
// Propagate the information from allocator bounds.
445389
GlobalProgramInfo.getABoundsInfo().performFlowAnalysis(&GlobalProgramInfo);
446390
}
447391

448-
// 4. Run intermediate tool hook to run visitors that need to be executed
392+
// 5. Run intermediate tool hook to run visitors that need to be executed
449393
// after constraint solving but before rewriting.
450-
std::unique_ptr<ToolAction> IMTool = newFrontendActionFactoryA<
451-
GenericAction<IntermediateToolHook, ProgramInfo>>(GlobalProgramInfo);
452-
if (IMTool) {
453-
int ToolExitCode = Tool.run(IMTool.get());
454-
if (ToolExitCode != 0)
455-
return false;
456-
} else
457-
llvm_unreachable("No Action");
394+
IntermediateToolHook ITH = IntermediateToolHook(GlobalProgramInfo, nullptr);
395+
unsigned int Errs = 0;
396+
for (auto &TU : ASTs) {
397+
TU->enableSourceFileDiagnostics();
398+
ITH.HandleTranslationUnit(TU->getASTContext());
399+
TU->getDiagnostics().getClient()->EndSourceFile();
400+
Errs += TU->getDiagnostics().getClient()->getNumErrors();
401+
}
402+
if (Errs > 0) return false;
458403

459404
if (AllTypes) {
460405
// Propagate data-flow information for Array pointers.
@@ -497,48 +442,19 @@ bool _3CInterface::solveConstraints() {
497442
return true;
498443
}
499444

500-
bool _3CInterface::writeConvertedFileToDisk(const std::string &FilePath) {
501-
std::lock_guard<std::mutex> Lock(InterfaceMutex);
502-
bool RetVal = false;
503-
if (std::find(SourceFiles.begin(), SourceFiles.end(), FilePath) !=
504-
SourceFiles.end()) {
505-
RetVal = true;
506-
std::vector<std::string> SourceFiles;
507-
SourceFiles.clear();
508-
SourceFiles.push_back(FilePath);
509-
// Don't use global tool. Create a new tool for give single file.
510-
ClangTool Tool(*CurrCompDB, SourceFiles);
511-
Tool.appendArgumentsAdjuster(getIgnoreCheckedPointerAdjuster());
512-
std::unique_ptr<ToolAction> RewriteTool =
513-
newFrontendActionFactoryA<RewriteAction<RewriteConsumer, ProgramInfo>>(
514-
GlobalProgramInfo, VerifyDiagnosticOutput);
515-
516-
if (RewriteTool) {
517-
int ToolExitCode = Tool.run(RewriteTool.get());
518-
if (ToolExitCode != 0)
519-
RetVal = false;
520-
}
521-
}
522-
GlobalProgramInfo.getPerfStats().endTotalTime();
523-
GlobalProgramInfo.getPerfStats().startTotalTime();
524-
return RetVal;
525-
}
526-
527445
bool _3CInterface::writeAllConvertedFilesToDisk() {
528446
std::lock_guard<std::mutex> Lock(InterfaceMutex);
529447

530-
ClangTool &Tool = getGlobalClangTool();
531-
532-
// Rewrite the input files.
533-
std::unique_ptr<ToolAction> RewriteTool =
534-
newFrontendActionFactoryA<RewriteAction<RewriteConsumer, ProgramInfo>>(
535-
GlobalProgramInfo, VerifyDiagnosticOutput);
536-
if (RewriteTool) {
537-
int ToolExitCode = Tool.run(RewriteTool.get());
538-
if (ToolExitCode != 0)
539-
return false;
540-
} else
541-
llvm_unreachable("No action");
448+
// 6. Rewrite the input files.
449+
RewriteConsumer RC = RewriteConsumer(GlobalProgramInfo);
450+
unsigned int Errs = 0;
451+
for (auto &TU : ASTs) {
452+
TU->enableSourceFileDiagnostics();
453+
RC.HandleTranslationUnit(TU->getASTContext());
454+
TU->getDiagnostics().getClient()->EndSourceFile();
455+
Errs += TU->getDiagnostics().getClient()->getNumErrors();
456+
}
457+
if (Errs > 0) return false;
542458

543459
GlobalProgramInfo.getPerfStats().endTotalTime();
544460
GlobalProgramInfo.getPerfStats().startTotalTime();

clang/lib/3C/PersistentSourceLoc.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,15 @@ PersistentSourceLoc PersistentSourceLoc::mkPSL(clang::SourceRange SR,
6666
FullSourceLoc TFSL(SR.getBegin(), SM);
6767
if (TFSL.isValid()) {
6868
const FileEntry *Fe = SM.getFileEntryForID(TFSL.getFileID());
69-
std::string ToConv = Fn;
70-
std::string FeAbsS = "";
71-
if (Fe != nullptr)
72-
ToConv = std::string(Fe->getName());
73-
getCanonicalFilePath(ToConv, FeAbsS);
69+
std::string FeAbsS = Fn;
70+
if (Fe != nullptr) {
71+
// Unlike in `emit` in RewriteUtils.cpp, we don't re-canonicalize the file
72+
// path because of the potential performance cost (mkPSL is called on many
73+
// AST nodes in each translation unit) and because we don't have a good
74+
// way to handle errors. If there is a problem, `emit` will detect it
75+
// before we actually write a file.
76+
FeAbsS = Fe->tryGetRealPathName().str();
77+
}
7478
Fn = std::string(sys::path::remove_leading_dotslash(FeAbsS));
7579
}
7680
PersistentSourceLoc PSL(Fn, FESL.getExpansionLineNumber(),

0 commit comments

Comments
 (0)