1 //===--- FrontendActions.cpp ----------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "flang/Frontend/FrontendActions.h"
10 #include "flang/Common/default-kinds.h"
11 #include "flang/Frontend/CompilerInstance.h"
12 #include "flang/Parser/parsing.h"
13 #include "flang/Parser/provenance.h"
14 #include "flang/Parser/source.h"
15 #include "flang/Semantics/semantics.h"
16 
17 using namespace Fortran::frontend;
18 
19 bool PrescanAction::BeginSourceFileAction(CompilerInstance &c1) {
20   CompilerInstance &ci = this->instance();
21 
22   std::string currentInputPath{GetCurrentFileOrBufferName()};
23 
24   Fortran::parser::Options parserOptions = ci.invocation().fortranOpts();
25 
26   if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) {
27     // Switch between fixed and free form format based on the input file
28     // extension.
29     //
30     // Ideally we should have all Fortran options set before entering this
31     // method (i.e. before processing any specific input files). However, we
32     // can't decide between fixed and free form based on the file extension
33     // earlier than this.
34     parserOptions.isFixedForm = currentInput().IsFixedForm();
35   }
36 
37   // Prescan. In case of failure, report and return.
38   ci.parsing().Prescan(currentInputPath, parserOptions);
39 
40   if (ci.parsing().messages().AnyFatalError()) {
41     const unsigned diagID = ci.diagnostics().getCustomDiagID(
42         clang::DiagnosticsEngine::Error, "Could not scan %0");
43     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
44     ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
45 
46     return false;
47   }
48 
49   return true;
50 }
51 
52 void InputOutputTestAction::ExecuteAction() {
53   CompilerInstance &ci = instance();
54 
55   // Create a stream for errors
56   std::string buf;
57   llvm::raw_string_ostream error_stream{buf};
58 
59   // Read the input file
60   Fortran::parser::AllSources &allSources{ci.allSources()};
61   std::string path{GetCurrentFileOrBufferName()};
62   const Fortran::parser::SourceFile *sf;
63   if (path == "-")
64     sf = allSources.ReadStandardInput(error_stream);
65   else
66     sf = allSources.Open(path, error_stream, std::optional<std::string>{"."s});
67   llvm::ArrayRef<char> fileContent = sf->content();
68 
69   // Output file descriptor to receive the contents of the input file.
70   std::unique_ptr<llvm::raw_ostream> os;
71 
72   // Copy the contents from the input file to the output file
73   if (!ci.IsOutputStreamNull()) {
74     // An output stream (outputStream_) was set earlier
75     ci.WriteOutputStream(fileContent.data());
76   } else {
77     // No pre-set output stream - create an output file
78     os = ci.CreateDefaultOutputFile(
79         /*binary=*/true, GetCurrentFileOrBufferName(), "txt");
80     if (!os)
81       return;
82     (*os) << fileContent.data();
83   }
84 }
85 
86 void PrintPreprocessedAction::ExecuteAction() {
87   std::string buf;
88   llvm::raw_string_ostream outForPP{buf};
89 
90   // Run the preprocessor
91   CompilerInstance &ci = this->instance();
92   ci.parsing().DumpCookedChars(outForPP);
93 
94   // If a pre-defined output stream exists, dump the preprocessed content there
95   if (!ci.IsOutputStreamNull()) {
96     // Send the output to the pre-defined output buffer.
97     ci.WriteOutputStream(outForPP.str());
98     return;
99   }
100 
101   // Print diagnostics from the preprocessor
102   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
103 
104   // Create a file and save the preprocessed output there
105   if (auto os{ci.CreateDefaultOutputFile(
106           /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())}) {
107     (*os) << outForPP.str();
108   } else {
109     llvm::errs() << "Unable to create the output file\n";
110     return;
111   }
112 }
113 
114 void ParseSyntaxOnlyAction::ExecuteAction() {
115   CompilerInstance &ci = this->instance();
116 
117   // Parse. In case of failure, report and return.
118   ci.parsing().Parse(llvm::outs());
119 
120   if (ci.parsing().messages().AnyFatalError()) {
121     unsigned diagID = ci.diagnostics().getCustomDiagID(
122         clang::DiagnosticsEngine::Error, "Could not parse %0");
123     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
124 
125     ci.parsing().messages().Emit(
126         llvm::errs(), this->instance().allCookedSources());
127     return;
128   }
129 
130   // Report the diagnostics from parsing
131   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
132 
133   auto &parseTree{*ci.parsing().parseTree()};
134 
135   // Prepare semantics
136   Fortran::semantics::Semantics semantics{ci.invocation().semanticsContext(),
137       parseTree, ci.parsing().cooked().AsCharBlock()};
138 
139   // Run semantic checks
140   semantics.Perform();
141 
142   // Report the diagnostics from the semantic checks
143   semantics.EmitMessages(ci.semaOutputStream());
144 
145   if (semantics.AnyFatalError()) {
146     unsigned DiagID = ci.diagnostics().getCustomDiagID(
147         clang::DiagnosticsEngine::Error, "Semantic errors in %0");
148     ci.diagnostics().Report(DiagID) << GetCurrentFileOrBufferName();
149   }
150 }
151 
152 void EmitObjAction::ExecuteAction() {
153   CompilerInstance &ci = this->instance();
154   unsigned DiagID = ci.diagnostics().getCustomDiagID(
155       clang::DiagnosticsEngine::Error, "code-generation is not available yet");
156   ci.diagnostics().Report(DiagID);
157 }
158