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::OmpClause & clause)38   bool Pre(const parser::OmpClause &clause) {
39     currStmt_ = clause.source;
40     return true;
41   }
Post(const parser::OmpClause &)42   void Post(const parser::OmpClause &) { currStmt_ = std::nullopt; }
Pre(const parser::OpenMPThreadprivate & dir)43   bool Pre(const parser::OpenMPThreadprivate &dir) {
44     currStmt_ = dir.source;
45     return true;
46   }
Post(const parser::OpenMPThreadprivate &)47   void Post(const parser::OpenMPThreadprivate &) { currStmt_ = std::nullopt; }
48   void Post(const parser::Name &name);
49 
50 private:
51   std::optional<SourceName> currStmt_; // current statement we are processing
52   std::multimap<const char *, const Symbol *> symbols_; // location to symbol
53   std::set<const Symbol *> symbolsDefined_; // symbols that have been processed
54   void Indent(llvm::raw_ostream &, int) const;
55 };
56 
PrintSymbols(const parser::CharBlock & location,llvm::raw_ostream & out,int indent)57 void SymbolDumpVisitor::PrintSymbols(
58     const parser::CharBlock &location, llvm::raw_ostream &out, int indent) {
59   std::set<const Symbol *> done; // prevent duplicates on this line
60   auto range{symbols_.equal_range(location.begin())};
61   for (auto it{range.first}; it != range.second; ++it) {
62     const auto *symbol{it->second};
63     if (done.insert(symbol).second) {
64       bool firstTime{symbolsDefined_.insert(symbol).second};
65       Indent(out, indent);
66       out << '!' << (firstTime ? "DEF"s : "REF"s) << ": ";
67       DumpForUnparse(out, *symbol, firstTime);
68       out << '\n';
69     }
70   }
71 }
72 
Indent(llvm::raw_ostream & out,int indent) const73 void SymbolDumpVisitor::Indent(llvm::raw_ostream &out, int indent) const {
74   for (int i{0}; i < indent; ++i) {
75     out << ' ';
76   }
77 }
78 
Post(const parser::Name & name)79 void SymbolDumpVisitor::Post(const parser::Name &name) {
80   if (const auto *symbol{name.symbol}) {
81     if (!symbol->has<MiscDetails>()) {
82       symbols_.emplace(currStmt_.value().begin(), symbol);
83     }
84   }
85 }
86 
UnparseWithSymbols(llvm::raw_ostream & out,const parser::Program & program,parser::Encoding encoding)87 void UnparseWithSymbols(llvm::raw_ostream &out, const parser::Program &program,
88     parser::Encoding encoding) {
89   SymbolDumpVisitor visitor;
90   parser::Walk(program, visitor);
91   parser::preStatementType preStatement{
92       [&](const parser::CharBlock &location, llvm::raw_ostream &out,
93           int indent) { visitor.PrintSymbols(location, out, indent); }};
94   parser::Unparse(out, program, encoding, false, true, &preStatement);
95 }
96 } // namespace Fortran::semantics
97