Author: Andrzej Warzynski Date: 2020-12-18T09:35:02Z New Revision: 7d246cb19db9fce65946fb4bac6e570787dbe78a
URL: https://github.com/llvm/llvm-project/commit/7d246cb19db9fce65946fb4bac6e570787dbe78a DIFF: https://github.com/llvm/llvm-project/commit/7d246cb19db9fce65946fb4bac6e570787dbe78a.diff LOG: [flang][driver] Add support for `-fsyntax-only` The behaviour triggered with this flag is consistent with `-fparse-only` in `flang` (i.e. the throwaway driver). This new spelling is consistent with Clang and gfortran, and was proposed and agreed on for the new driver in [1]. This patch also adds some minimal logic to communicate whether the semantic checks have failed or not. When semantic checks fail, a frontend driver error is generated. The return code from the frontend driver is then determined by checking the driver diagnostics - the presence of driver errors means that the compilation has failed. This logic is consistent with `clang -cc1`. [1] http://lists.llvm.org/pipermail/flang-dev/2020-November/000588.html Differential Revision: https://reviews.llvm.org/D92854 Added: flang/test/Flang-Driver/syntax-only.f90 Modified: clang/include/clang/Driver/Options.td flang/include/flang/Frontend/CompilerInstance.h flang/include/flang/Frontend/FrontendActions.h flang/include/flang/Frontend/FrontendOptions.h flang/lib/Frontend/CMakeLists.txt flang/lib/Frontend/CompilerInstance.cpp flang/lib/Frontend/CompilerInvocation.cpp flang/lib/Frontend/FrontendActions.cpp flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp flang/unittests/Frontend/PrintPreprocessedTest.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 3e4fbf1fbb8f..63a5b5484f0f 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2287,7 +2287,7 @@ defm strict_vtable_pointers : BoolFOption<"strict-vtable-pointers", ResetBy<NegFlag>>; def fstrict_overflow : Flag<["-"], "fstrict-overflow">, Group<f_Group>; def fsyntax_only : Flag<["-"], "fsyntax-only">, - Flags<[NoXarchOption,CoreOption,CC1Option]>, Group<Action_Group>; + Flags<[NoXarchOption,CoreOption,CC1Option,FC1Option]>, Group<Action_Group>; def ftabstop_EQ : Joined<["-"], "ftabstop=">, Group<f_Group>; def ftemplate_depth_EQ : Joined<["-"], "ftemplate-depth=">, Group<f_Group>; def ftemplate_depth_ : Joined<["-"], "ftemplate-depth-">, Group<f_Group>; diff --git a/flang/include/flang/Frontend/CompilerInstance.h b/flang/include/flang/Frontend/CompilerInstance.h index 21ef9f0dcd47..b00b5cd4479a 100644 --- a/flang/include/flang/Frontend/CompilerInstance.h +++ b/flang/include/flang/Frontend/CompilerInstance.h @@ -12,6 +12,7 @@ #include "flang/Frontend/FrontendAction.h" #include "flang/Parser/parsing.h" #include "flang/Parser/provenance.h" +#include "flang/Semantics/semantics.h" #include "llvm/Support/raw_ostream.h" namespace Fortran::frontend { @@ -28,6 +29,12 @@ class CompilerInstance { std::shared_ptr<Fortran::parser::Parsing> parsing_; + /// The stream for diagnostics from Semantics + llvm::raw_ostream *semaOutputStream_ = &llvm::errs(); + + /// The stream for diagnostics from Semantics if owned, otherwise nullptr. + std::unique_ptr<llvm::raw_ostream> ownedSemaOutputStream_; + /// The diagnostics engine instance. llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics_; @@ -77,6 +84,11 @@ class CompilerInstance { bool HasAllSources() const { return allSources_ != nullptr; } + parser::AllCookedSources &allCookedSources() { + assert(allCookedSources_ && "Compiler instance has no AllCookedSources!"); + return *allCookedSources_; + }; + /// } /// @name Parser Operations /// { @@ -84,6 +96,19 @@ class CompilerInstance { /// Return parsing to be used by Actions. Fortran::parser::Parsing &parsing() const { return *parsing_; } + /// } + /// @name Semantic analysis + /// { + + /// Replace the current stream for verbose output. + void set_semaOutputStream(llvm::raw_ostream &Value); + + /// Replace the current stream for verbose output. + void set_semaOutputStream(std::unique_ptr<llvm::raw_ostream> Value); + + /// Get the current stream for verbose output. + llvm::raw_ostream &semaOutputStream() { return *semaOutputStream_; } + /// } /// @name High-Level Operations /// { diff --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h index db5afe4da7eb..3eea94b60bf5 100644 --- a/flang/include/flang/Frontend/FrontendActions.h +++ b/flang/include/flang/Frontend/FrontendActions.h @@ -25,6 +25,10 @@ class PrintPreprocessedAction : public FrontendAction { void ExecuteAction() override; }; +class ParseSyntaxOnlyAction : public FrontendAction { + void ExecuteAction() override; +}; + } // namespace Fortran::frontend #endif // LLVM_FLANG_FRONTEND_FRONTENDACTIONS_H diff --git a/flang/include/flang/Frontend/FrontendOptions.h b/flang/include/flang/Frontend/FrontendOptions.h index ac8f01d2a173..d167f094955c 100644 --- a/flang/include/flang/Frontend/FrontendOptions.h +++ b/flang/include/flang/Frontend/FrontendOptions.h @@ -1,4 +1,4 @@ -//===- FrontendOptions.h ----------------------------------------*- C -*-===// +//===- FrontendOptions.h ----------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -24,7 +24,11 @@ enum ActionKind { /// -E mode. PrintPreprocessedInput, - /// TODO: RunPreprocessor, ParserSyntaxOnly, EmitLLVM, EmitLLVMOnly, + + /// -fsyntax-only + ParseSyntaxOnly, + + /// TODO: RunPreprocessor, EmitLLVM, EmitLLVMOnly, /// EmitCodeGenOnly, EmitAssembly, (...) }; @@ -34,6 +38,8 @@ inline const char *GetActionKindName(const ActionKind ak) { return "InputOutputTest"; case PrintPreprocessedInput: return "PrintPreprocessedInput"; + case ParseSyntaxOnly: + return "ParseSyntaxOnly"; default: return "<unknown ActionKind>"; // TODO: diff --git a/flang/lib/Frontend/CMakeLists.txt b/flang/lib/Frontend/CMakeLists.txt index 22e38595ea4c..095271f567f4 100644 --- a/flang/lib/Frontend/CMakeLists.txt +++ b/flang/lib/Frontend/CMakeLists.txt @@ -13,6 +13,8 @@ add_flang_library(flangFrontend LINK_LIBS FortranParser + FortranSemantics + FortranCommon clangBasic clangDriver diff --git a/flang/lib/Frontend/CompilerInstance.cpp b/flang/lib/Frontend/CompilerInstance.cpp index f1ca55fb7374..f473bcd19546 100644 --- a/flang/lib/Frontend/CompilerInstance.cpp +++ b/flang/lib/Frontend/CompilerInstance.cpp @@ -11,6 +11,7 @@ #include "flang/Frontend/TextDiagnosticPrinter.h" #include "flang/Parser/parsing.h" #include "flang/Parser/provenance.h" +#include "flang/Semantics/semantics.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" @@ -39,6 +40,17 @@ void CompilerInstance::set_invocation( invocation_ = std::move(value); } +void CompilerInstance::set_semaOutputStream(raw_ostream &Value) { + ownedSemaOutputStream_.release(); + semaOutputStream_ = &Value; +} + +void CompilerInstance::set_semaOutputStream( + std::unique_ptr<raw_ostream> Value) { + ownedSemaOutputStream_.swap(Value); + semaOutputStream_ = ownedSemaOutputStream_.get(); +} + void CompilerInstance::AddOutputFile(OutputFile &&outFile) { outputFiles_.push_back(std::move(outFile)); } @@ -140,7 +152,7 @@ bool CompilerInstance::ExecuteAction(FrontendAction &act) { act.EndSourceFile(); } } - return true; + return !diagnostics().getClient()->getNumErrors(); } void CompilerInstance::CreateDiagnostics( diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp index 8a6f66151324..db3ae74128cd 100644 --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -93,6 +93,9 @@ static InputKind ParseFrontendArgs(FrontendOptions &opts, case clang::driver::options::OPT_E: opts.programAction_ = PrintPreprocessedInput; break; + case clang::driver::options::OPT_fsyntax_only: + opts.programAction_ = ParseSyntaxOnly; + break; // TODO: // case clang::driver::options::OPT_emit_obj: diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp index 9c951d964c01..b34dae7cbf17 100644 --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -7,10 +7,12 @@ //===----------------------------------------------------------------------===// #include "flang/Frontend/FrontendActions.h" +#include "flang/Common/default-kinds.h" #include "flang/Frontend/CompilerInstance.h" #include "flang/Parser/parsing.h" #include "flang/Parser/provenance.h" #include "flang/Parser/source.h" +#include "flang/Semantics/semantics.h" using namespace Fortran::frontend; @@ -68,3 +70,33 @@ void PrintPreprocessedAction::ExecuteAction() { return; } } + +void ParseSyntaxOnlyAction::ExecuteAction() { + CompilerInstance &ci = this->instance(); + + // TODO: These should be specifiable by users. For now just use the defaults. + common::LanguageFeatureControl features; + Fortran::common::IntrinsicTypeDefaultKinds defaultKinds; + + // Parse + ci.parsing().Parse(llvm::outs()); + auto &parseTree{*ci.parsing().parseTree()}; + + // Prepare semantics + Fortran::semantics::SemanticsContext semanticsContext{ + defaultKinds, features, ci.allCookedSources()}; + Fortran::semantics::Semantics semantics{ + semanticsContext, parseTree, ci.parsing().cooked().AsCharBlock()}; + + // Run semantic checks + semantics.Perform(); + + // Report the diagnostics from the semantic checks + semantics.EmitMessages(ci.semaOutputStream()); + + if (semantics.AnyFatalError()) { + unsigned DiagID = ci.diagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Error, "semantic errors in %0"); + ci.diagnostics().Report(DiagID) << GetCurrentFileOrBufferName(); + } +} diff --git a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 52cfc17829e5..0315fd5dd2fb 100644 --- a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -32,6 +32,9 @@ static std::unique_ptr<FrontendAction> CreateFrontendBaseAction( case PrintPreprocessedInput: return std::make_unique<PrintPreprocessedAction>(); break; + case ParseSyntaxOnly: + return std::make_unique<ParseSyntaxOnlyAction>(); + break; default: break; // TODO: diff --git a/flang/test/Flang-Driver/syntax-only.f90 b/flang/test/Flang-Driver/syntax-only.f90 new file mode 100644 index 000000000000..4b435e779d56 --- /dev/null +++ b/flang/test/Flang-Driver/syntax-only.f90 @@ -0,0 +1,9 @@ +! RUN: not %flang-new -fc1 -fsyntax-only %s 2>&1 | FileCheck %s +! RUN: not %f18 -fparse-only %s 2>&1 | FileCheck %s + +! REQUIRES: new-flang-driver + +! CHECK: IF statement is not allowed in IF statement +! CHECK: semantic errors in {{.*}}syntax-only.f90 +IF (A > 0.0) IF (B < 0.0) A = LOG (A) +END diff --git a/flang/unittests/Frontend/PrintPreprocessedTest.cpp b/flang/unittests/Frontend/PrintPreprocessedTest.cpp index 5ba3c702728f..78161f691eff 100644 --- a/flang/unittests/Frontend/PrintPreprocessedTest.cpp +++ b/flang/unittests/Frontend/PrintPreprocessedTest.cpp @@ -76,4 +76,60 @@ TEST(FrontendAction, PrintPreprocessedInput) { llvm::sys::fs::remove(inputFile); compInst.ClearOutputFiles(/*EraseFiles=*/true); } + +TEST(FrontendAction, ParseSyntaxOnly) { + std::string inputFile = "test-file.f"; + std::error_code ec; + + // 1. Create the input file for the file manager + // AllSources (which is used to manage files inside every compiler instance), + // works with paths. This means that it requires a physical file. Create one. + std::unique_ptr<llvm::raw_fd_ostream> os{ + new llvm::raw_fd_ostream(inputFile, ec, llvm::sys::fs::OF_None)}; + if (ec) + FAIL() << "Fail to create the file need by the test"; + + // Populate the input file with the pre-defined input and flush it. + *(os) << "! if_stmt.f90:\n" + << "IF (A > 0.0) IF (B < 0.0) A = LOG (A)\n" + << "END"; + os.reset(); + + // Get the path of the input file + llvm::SmallString<64> cwd; + if (std::error_code ec = llvm::sys::fs::current_path(cwd)) + FAIL() << "Failed to obtain the current working directory"; + std::string testFilePath(cwd.c_str()); + testFilePath += "/" + inputFile; + + // 2. Prepare the compiler (CompilerInvocation + CompilerInstance) + CompilerInstance compInst; + compInst.CreateDiagnostics(); + auto invocation = std::make_shared<CompilerInvocation>(); + invocation->frontendOpts().programAction_ = ParseSyntaxOnly; + + compInst.set_invocation(std::move(invocation)); + compInst.frontendOpts().inputs_.push_back( + FrontendInputFile(testFilePath, Language::Fortran)); + + // 3. Set-up the output stream for the semantic diagnostics. + llvm::SmallVector<char, 256> outputDiagBuffer; + std::unique_ptr<llvm::raw_pwrite_stream> outputStream( + new llvm::raw_svector_ostream(outputDiagBuffer)); + compInst.set_semaOutputStream(std::move(outputStream)); + + // 4. Execute the ParseSyntaxOnly action + bool success = ExecuteCompilerInvocation(&compInst); + + // 5. Validate the expected output + EXPECT_FALSE(success); + EXPECT_TRUE(!outputDiagBuffer.empty()); + EXPECT_TRUE( + llvm::StringRef(outputDiagBuffer.data()) + .startswith( + ":2:14: error: IF statement is not allowed in IF statement\n")); + + // 6. Clear the input files. + llvm::sys::fs::remove(inputFile); +} } // namespace _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits