1 //===-- lib/Semantics/unparse-with-symbols.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/Semantics/unparse-with-symbols.h"
10 #include "flang/Parser/parse-tree-visitor.h"
11 #include "flang/Parser/parse-tree.h"
12 #include "flang/Parser/unparse.h"
13 #include "flang/Semantics/symbol.h"
14 #include "llvm/Support/raw_ostream.h"
15 #include <map>
16 #include <set>
17 
18 namespace Fortran::semantics {
19 
20 // Walk the parse tree and collection information about which statements
21 // reference symbols. Then PrintSymbols outputs information by statement.
22 // The first reference to a symbol is treated as its definition and more
23 // information is included.
24 class SymbolDumpVisitor {
25 public:
26   // Write out symbols referenced at this statement.
27   void PrintSymbols(const parser::CharBlock &, llvm::raw_ostream &, int);
28 
Pre(const T &)29   template <typename T> bool Pre(const T &) { return true; }
Post(const T &)30   template <typename T> void Post(const T &) {}
Pre(const parser::Statement<T> & stmt)31   template <typename T> bool Pre(const parser::Statement<T> &stmt) {
32     currStmt_ = stmt.source;
33     return true;
34   }
Post(const parser::Statement<T> &)35   template <typename T> void Post(const parser::Statement<T> &) {
36     currStmt_ = std::nullopt;
37   }
Pre(const parser::AccClause & clause)38   bool Pre(const parser::AccClause &clause) {
39     currStmt_ = clause.source;
40     return true;
41   }
Post(const parser::AccClause &)42   void Post(const parser::AccClause &) { currStmt_ = std::nullopt; }
Pre(const parser::OmpClause & clause)43   bool Pre(const parser::OmpClause &clause) {
44     currStmt_ = clause.source;
45     return true;
46   }
Post(const parser::OmpClause &)47   void Post(const parser::OmpClause &) { currStmt_ = std::nullopt; }
Pre(const parser::OpenMPThreadprivate & dir)48   bool Pre(const parser::OpenMPThreadprivate &dir) {
49     currStmt_ = dir.source;
50     return true;
51   }
Post(const parser::OpenMPThreadprivate &)52   void Post(const parser::OpenMPThreadprivate &) { currStmt_ = std::nullopt; }
53   void Post(const parser::Name &name);
54 
55 private:
56   std::optional<SourceName> currStmt_; // current statement we are processing
57   std::multimap<const char *, const Symbol *> symbols_; // location to symbol
58   std::set<const Symbol *> symbolsDefined_; // symbols that have been processed
59   void Indent(llvm::raw_ostream &, int) const;
60 };
61 
PrintSymbols(const parser::CharBlock & location,llvm::raw_ostream & out,int indent)62 void SymbolDumpVisitor::PrintSymbols(
63     const parser::CharBlock &location, llvm::raw_ostream &out, int indent) {
64   std::set<const Symbol *> done; // prevent duplicates on this line
65   auto range{symbols_.equal_range(location.begin())};
66   for (auto it{range.first}; it != range.second; ++it) {
67     const auto *symbol{it->second};
68     if (done.insert(symbol).second) {
69       bool firstTime{symbolsDefined_.insert(symbol).second};
70       Indent(out, indent);
71       out << '!' << (firstTime ? "DEF"s : "REF"s) << ": ";
72       DumpForUnparse(out, *symbol, firstTime);
73       out << '\n';
74     }
75   }
76 }
77 
Indent(llvm::raw_ostream & out,int indent) const78 void SymbolDumpVisitor::Indent(llvm::raw_ostream &out, int indent) const {
79   for (int i{0}; i < indent; ++i) {
80     out << ' ';
81   }
82 }
83 
Post(const parser::Name & name)84 void SymbolDumpVisitor::Post(const parser::Name &name) {
85   if (const auto *symbol{name.symbol}) {
86     if (!symbol->has<MiscDetails>()) {
87       symbols_.emplace(currStmt_.value().begin(), symbol);
88     }
89   }
90 }
91 
UnparseWithSymbols(llvm::raw_ostream & out,const parser::Program & program,parser::Encoding encoding)92 void UnparseWithSymbols(llvm::raw_ostream &out, const parser::Program &program,
93     parser::Encoding encoding) {
94   SymbolDumpVisitor visitor;
95   parser::Walk(program, visitor);
96   parser::preStatementType preStatement{
97       [&](const parser::CharBlock &location, llvm::raw_ostream &out,
98           int indent) { visitor.PrintSymbols(location, out, indent); }};
99   parser::Unparse(out, program, encoding, false, true, &preStatement);
100 }
101 } // namespace Fortran::semantics
102