Skip to content

Commit 6b85e35

Browse files
author
Nathan Hawes
authored
Merge pull request #28388 from nathawes/swift-ide-test-syntactic-walker-setup
[swift-ide-test] Mimic SourceKit's setup when testing syntactic requests
2 parents 596961c + ddd714e commit 6b85e35

File tree

1 file changed

+125
-64
lines changed

1 file changed

+125
-64
lines changed

tools/swift-ide-test/swift-ide-test.cpp

Lines changed: 125 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "swift/IDE/IDERequests.h"
4747
#include "swift/Index/Index.h"
4848
#include "swift/Sema/IDETypeChecking.h"
49+
#include "swift/SyntaxParse/SyntaxTreeCreator.h"
4950
#include "swift/Markup/Markup.h"
5051
#include "swift/Config.h"
5152
#include "clang/Rewrite/Core/RewriteBuffer.h"
@@ -717,25 +718,35 @@ removeCodeCompletionTokens(llvm::MemoryBuffer *Input,
717718
Input->getBufferIdentifier()));
718719
}
719720

721+
/// Returns true on error
722+
static bool setBufferForFile(StringRef SourceFilename,
723+
std::unique_ptr<llvm::MemoryBuffer> &Buffer) {
724+
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
725+
llvm::MemoryBuffer::getFile(SourceFilename);
726+
if (!FileBufOrErr) {
727+
llvm::errs() << "error opening input file '" << SourceFilename << "':\n"
728+
<< " " << FileBufOrErr.getError().message() << '\n';
729+
return true;
730+
}
731+
Buffer = std::move(FileBufOrErr.get());
732+
return false;
733+
}
734+
720735
static bool doCodeCompletionImpl(
721736
CodeCompletionCallbacksFactory *callbacksFactory,
722737
const CompilerInvocation &InitInvok,
723738
StringRef SourceFilename,
724739
StringRef SecondSourceFileName,
725740
StringRef CodeCompletionToken,
726741
bool CodeCompletionDiagnostics) {
727-
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
728-
llvm::MemoryBuffer::getFile(SourceFilename);
729-
if (!FileBufOrErr) {
730-
llvm::errs() << "error opening input file: "
731-
<< FileBufOrErr.getError().message() << '\n';
742+
std::unique_ptr<llvm::MemoryBuffer> FileBuf;
743+
if (setBufferForFile(SourceFilename, FileBuf))
732744
return 1;
733-
}
734745

735746
unsigned Offset;
736747

737748
std::unique_ptr<llvm::MemoryBuffer> CleanFile(removeCodeCompletionTokens(
738-
FileBufOrErr.get().get(), CodeCompletionToken, &Offset));
749+
FileBuf.get(), CodeCompletionToken, &Offset));
739750

740751
if (Offset == ~0U) {
741752
llvm::errs() << "could not find code completion token \""
@@ -843,15 +854,11 @@ static int doCodeCompletion(const CompilerInvocation &InitInvok,
843854

844855
static int doREPLCodeCompletion(const CompilerInvocation &InitInvok,
845856
StringRef SourceFilename) {
846-
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
847-
llvm::MemoryBuffer::getFile(SourceFilename);
848-
if (!FileBufOrErr) {
849-
llvm::errs() << "error opening input file: "
850-
<< FileBufOrErr.getError().message() << '\n';
857+
std::unique_ptr<llvm::MemoryBuffer> FileBuf;
858+
if (setBufferForFile(SourceFilename, FileBuf))
851859
return 1;
852-
}
853860

854-
StringRef BufferText = FileBufOrErr.get()->getBuffer();
861+
StringRef BufferText = FileBuf->getBuffer();
855862
// Drop a single newline character from the buffer.
856863
if (BufferText.endswith("\n"))
857864
BufferText = BufferText.drop_back(1);
@@ -1057,38 +1064,74 @@ static int doSyntaxColoring(const CompilerInvocation &InitInvok,
10571064
CompilerInvocation Invocation(InitInvok);
10581065
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename);
10591066
Invocation.getLangOptions().DisableAvailabilityChecking = false;
1060-
1061-
CompilerInstance CI;
1062-
1063-
// Display diagnostics to stderr.
1064-
PrintingDiagnosticConsumer PrintDiags;
1065-
CI.addDiagnosticConsumer(&PrintDiags);
10661067
Invocation.getLangOptions().Playground = Playground;
10671068
Invocation.getLangOptions().CollectParsedToken = true;
10681069
Invocation.getLangOptions().BuildSyntaxTree = true;
1069-
if (CI.setup(Invocation))
1070-
return 1;
1071-
registerIDERequestFunctions(CI.getASTContext().evaluator);
1072-
if (!RunTypeChecker)
1073-
CI.performParseOnly();
1074-
else
1070+
1071+
// Display diagnostics to stderr.
1072+
PrintingDiagnosticConsumer PrintDiags;
1073+
1074+
if (RunTypeChecker) {
1075+
CompilerInstance CI;
1076+
CI.addDiagnosticConsumer(&PrintDiags);
1077+
if (CI.setup(Invocation))
1078+
return 1;
10751079
CI.performSema();
10761080

1077-
unsigned BufID = CI.getInputBufferIDs().back();
1078-
SourceFile *SF = nullptr;
1079-
for (auto Unit : CI.getMainModule()->getFiles()) {
1080-
SF = dyn_cast<SourceFile>(Unit);
1081-
if (SF)
1082-
break;
1083-
}
1084-
assert(SF && "no source file?");
1081+
unsigned BufID = CI.getInputBufferIDs().back();
1082+
SourceFile *SF = nullptr;
1083+
for (auto Unit : CI.getMainModule()->getFiles()) {
1084+
SF = dyn_cast<SourceFile>(Unit);
1085+
if (SF)
1086+
break;
1087+
}
1088+
assert(SF && "no source file?");
1089+
1090+
ide::SyntaxModelContext ColorContext(*SF);
1091+
PrintSyntaxColorWalker ColorWalker(CI.getSourceMgr(), BufID, llvm::outs(),
1092+
TerminalOutput);
1093+
ColorContext.walk(ColorWalker);
1094+
ColorWalker.finished();
1095+
} else {
1096+
// SourceKit doesn't set up a compiler instance at all for its syntactic
1097+
// requests, just the parser. We try to mimic that setup here to help catch
1098+
// any cases where the walker might inadvertently rely on the name lookup or
1099+
// other semantic functionality via the request evaluator.
1100+
std::unique_ptr<llvm::MemoryBuffer> FileBuf;
1101+
if (setBufferForFile(SourceFilename, FileBuf))
1102+
return 1;
1103+
1104+
SourceManager SM;
1105+
unsigned BufferID = SM.addNewSourceBuffer(std::move(FileBuf));
10851106

1086-
ide::SyntaxModelContext ColorContext(*SF);
1087-
PrintSyntaxColorWalker ColorWalker(CI.getSourceMgr(), BufID, llvm::outs(),
1088-
TerminalOutput);
1089-
ColorContext.walk(ColorWalker);
1090-
ColorWalker.finished();
1107+
RC<SyntaxArena> syntaxArena{new syntax::SyntaxArena()};
1108+
std::shared_ptr<SyntaxTreeCreator> SynTreeCreator =
1109+
std::make_shared<SyntaxTreeCreator>(
1110+
SM, BufferID, Invocation.getMainFileSyntaxParsingCache(),
1111+
syntaxArena);
10911112

1113+
ParserUnit Parser(SM, SourceFileKind::Main, BufferID,
1114+
Invocation.getLangOptions(),
1115+
Invocation.getTypeCheckerOptions(),
1116+
Invocation.getModuleName(),
1117+
SynTreeCreator,
1118+
Invocation.getMainFileSyntaxParsingCache());
1119+
1120+
registerParseRequestFunctions(Parser.getParser().Context.evaluator);
1121+
registerTypeCheckerRequestFunctions(Parser.getParser().Context.evaluator);
1122+
1123+
// Collecting syntactic information shouldn't evaluate # conditions.
1124+
Parser.getParser().State->PerformConditionEvaluation = false;
1125+
Parser.getDiagnosticEngine().addConsumer(PrintDiags);
1126+
1127+
(void)Parser.parse();
1128+
1129+
ide::SyntaxModelContext ColorContext(Parser.getSourceFile());
1130+
PrintSyntaxColorWalker ColorWalker(SM, BufferID, llvm::outs(),
1131+
TerminalOutput);
1132+
ColorContext.walk(ColorWalker);
1133+
ColorWalker.finished();
1134+
}
10921135
return 0;
10931136
}
10941137

@@ -1280,25 +1323,51 @@ class StructureAnnotator : public ide::SyntaxModelWalker {
12801323

12811324
static int doStructureAnnotation(const CompilerInvocation &InitInvok,
12821325
StringRef SourceFilename) {
1326+
std::unique_ptr<llvm::MemoryBuffer> FileBuf;
1327+
if (setBufferForFile(SourceFilename, FileBuf))
1328+
return 1;
1329+
12831330
CompilerInvocation Invocation(InitInvok);
12841331
Invocation.getLangOptions().BuildSyntaxTree = true;
12851332
Invocation.getLangOptions().CollectParsedToken = true;
12861333
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(SourceFilename);
12871334

1288-
CompilerInstance CI;
1335+
// Structure annotation is run as a purely syntactic request by SourceKit. It
1336+
// doesn't set up a compiler instance at all, just the parser. We try to mimic
1337+
// that setup here to help catch any cases where the walker might inadvertently
1338+
// rely on the name lookup or other semantic functionality via the request
1339+
// evaluator.
1340+
SourceManager SM;
1341+
unsigned BufferID = SM.addNewSourceBuffer(std::move(FileBuf));
1342+
1343+
RC<SyntaxArena> syntaxArena{new syntax::SyntaxArena()};
1344+
std::shared_ptr<SyntaxTreeCreator> SynTreeCreator =
1345+
std::make_shared<SyntaxTreeCreator>(
1346+
SM, BufferID, Invocation.getMainFileSyntaxParsingCache(),
1347+
syntaxArena);
1348+
1349+
ParserUnit Parser(SM, SourceFileKind::Main, BufferID,
1350+
Invocation.getLangOptions(),
1351+
Invocation.getTypeCheckerOptions(),
1352+
Invocation.getModuleName(),
1353+
SynTreeCreator,
1354+
Invocation.getMainFileSyntaxParsingCache());
1355+
1356+
registerParseRequestFunctions(Parser.getParser().Context.evaluator);
1357+
registerTypeCheckerRequestFunctions(
1358+
Parser.getParser().Context.evaluator);
1359+
1360+
// Collecting syntactic information shouldn't evaluate # conditions.
1361+
Parser.getParser().State->PerformConditionEvaluation = false;
12891362

12901363
// Display diagnostics to stderr.
12911364
PrintingDiagnosticConsumer PrintDiags;
1292-
CI.addDiagnosticConsumer(&PrintDiags);
1293-
if (CI.setup(Invocation))
1294-
return 1;
1295-
registerIDERequestFunctions(CI.getASTContext().evaluator);
1296-
CI.performParseOnly();
1365+
Parser.getDiagnosticEngine().addConsumer(PrintDiags);
12971366

1298-
unsigned BufID = CI.getInputBufferIDs().back();
1299-
ide::SyntaxModelContext StructureContext(
1300-
CI.getMainModule()->getMainSourceFile(SourceFileKind::Main));
1301-
StructureAnnotator Annotator(CI.getSourceMgr(), BufID);
1367+
(void)Parser.parse();
1368+
1369+
ide::SyntaxModelContext StructureContext(Parser.getSourceFile());
1370+
StructureAnnotator Annotator(SM, BufferID);
13021371
StructureContext.walk(Annotator);
13031372
Annotator.printResult(llvm::outs());
13041373
return 0;
@@ -1561,17 +1630,13 @@ static int doSemanticAnnotation(const CompilerInvocation &InitInvok,
15611630
}
15621631

15631632
static int doInputCompletenessTest(StringRef SourceFilename) {
1564-
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
1565-
llvm::MemoryBuffer::getFile(SourceFilename);
1566-
if (!FileBufOrErr) {
1567-
llvm::errs() << "error opening input file: "
1568-
<< FileBufOrErr.getError().message() << '\n';
1633+
std::unique_ptr<llvm::MemoryBuffer> FileBuf;
1634+
if (setBufferForFile(SourceFilename, FileBuf))
15691635
return 1;
1570-
}
15711636

15721637
llvm::raw_ostream &OS = llvm::outs();
15731638
OS << SourceFilename << ": ";
1574-
if (isSourceInputComplete(std::move(FileBufOrErr.get()),
1639+
if (isSourceInputComplete(std::move(FileBuf),
15751640
SourceFileKind::REPL).IsComplete) {
15761641
OS << "IS_COMPLETE\n";
15771642
} else {
@@ -3102,16 +3167,12 @@ static int doTestCreateCompilerInvocation(ArrayRef<const char *> Args) {
31023167
}
31033168

31043169
static int doTestCompilerInvocationFromModule(StringRef ModuleFilePath) {
3105-
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
3106-
llvm::MemoryBuffer::getFile(ModuleFilePath);
3107-
if (!FileBufOrErr) {
3108-
llvm::errs() << "error opening input file: "
3109-
<< FileBufOrErr.getError().message() << '\n';
3110-
return -1;
3111-
}
3170+
std::unique_ptr<llvm::MemoryBuffer> FileBuf;
3171+
if (setBufferForFile(ModuleFilePath, FileBuf))
3172+
return 1;
31123173

31133174
CompilerInvocation CI;
3114-
StringRef Data = FileBufOrErr.get()->getBuffer();
3175+
StringRef Data = FileBuf->getBuffer();
31153176
static_assert(static_cast<int>(serialization::Status::Valid) == 0,
31163177
"Status::Valid should be a successful exit");
31173178
return static_cast<int>(CI.loadFromSerializedAST(Data));

0 commit comments

Comments
 (0)