[flang-commits] [flang] 7d246cb - [flang][driver] Add support for `-fsyntax-only`

Andrzej Warzynski via flang-commits flang-commits at lists.llvm.org
Fri Dec 18 01:48:18 PST 2020


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


        


More information about the flang-commits mailing list