1 /*
2  * RFunctionInformation.hpp
3  *
4  * Copyright (C) 2021 by RStudio, PBC
5  *
6  * Unless you have received this program directly from RStudio pursuant
7  * to the terms of a commercial license agreement with RStudio, then
8  * this program is licensed to you under the terms of version 3 of the
9  * GNU Affero General Public License. This program is distributed WITHOUT
10  * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12  * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13  *
14  */
15 
16 #ifndef CORE_R_UTIL_R_FUNCTION_INFORMATION_HPP
17 #define CORE_R_UTIL_R_FUNCTION_INFORMATION_HPP
18 
19 // Utility classes used for collecting information about R packages
20 // (their functions and what they do)
21 
22 #include <vector>
23 #include <string>
24 #include <map>
25 
26 #include <core/Log.hpp>
27 #include <core/collection/Position.hpp>
28 
29 #include <boost/optional.hpp>
30 #include <boost/logic/tribool.hpp>
31 
32 namespace rstudio {
33 namespace core {
34 namespace r_util {
35 
36 struct Binding
37 {
Bindingrstudio::core::r_util::Binding38    Binding(const std::string& name,
39            const std::string& origin)
40       : name(name), origin(origin)
41    {}
42 
operator <<(std::ostream & os,const Binding & self)43    friend std::ostream& operator <<(std::ostream& os,
44                                     const Binding& self)
45    {
46       os << "[" << self.origin << "::" << self.name << "]";
47       return os;
48    }
49 
50    std::string name;
51    std::string origin;
52 };
53 
54 class FormalInformation
55 {
56 public:
57 
58    // ctor -- must be initialized with a name;
59    // all other information is optional
FormalInformation(const std::string & name)60    explicit FormalInformation(const std::string& name)
61       : name_(name)
62    {}
63 
setDefaultValue(const std::string & defaultValue)64    void setDefaultValue(const std::string& defaultValue)
65    {
66       hasDefault_ = true;
67       defaultValue_ = defaultValue;
68    }
69 
setHasDefaultValue(bool value)70    void setHasDefaultValue(bool value)
71    {
72       hasDefault_ = value;
73    }
74 
name() const75    const std::string& name() const { return name_; }
defaultValue() const76    const boost::optional<std::string>& defaultValue() const { return defaultValue_; }
hasDefault() const77    boost::tribool hasDefault() const { return hasDefault_; }
isUsed() const78    bool isUsed() const { return bool(isUsed_); }
setIsUsed(bool value)79    void setIsUsed(bool value) { isUsed_ = value; }
isMissingnessHandled() const80    bool isMissingnessHandled() const { return bool(isMissingnessHandled_); }
setMissingnessHandled(bool value)81    void setMissingnessHandled(bool value) { isMissingnessHandled_ = value; }
82 
83 private:
84    std::string name_;
85 
86    // NOTE: It is possible for us to know that a particular
87    // function has a default value, but not what that default
88    // value is, hence why we have separate fields here.
89    boost::optional<std::string> defaultValue_;
90    boost::tribool hasDefault_;
91 
92    // Whether this formal is used in the body of its associated function
93    boost::tribool isUsed_;
94 
95    // Whether this formal is tested in a `missing()` call
96    boost::tribool isMissingnessHandled_;
97 
98    // private c-tor used as dummy 'no such formal', for friend classes
99    friend class FunctionInformation;
FormalInformation()100    FormalInformation() {}
101 };
102 
103 class FunctionInformation
104 {
105 public:
106 
107    // default ctor: we may not know the original binding
108    // for this function
FunctionInformation()109    FunctionInformation()
110    {}
111 
112    // binding ctor: gives the 'origin' of this function name
113    // (name + 'origin', which could be a package, namespace, env, ...)
FunctionInformation(const std::string & name,const std::string & origin)114    FunctionInformation(const std::string& name,
115                        const std::string& origin)
116       : binding_(Binding(name, origin))
117    {}
118 
addFormal(const std::string & name)119    void addFormal(const std::string& name)
120    {
121       formals_.push_back(FormalInformation(name));
122       formalNames_.push_back(name);
123    }
124 
addFormal(const FormalInformation & info)125    void addFormal(const FormalInformation& info)
126    {
127       formals_.push_back(info);
128       formalNames_.push_back(info.name());
129    }
130 
isPrimitive()131    bool isPrimitive()
132    {
133       return bool(isPrimitive_);
134    }
135 
setIsPrimitive(bool isPrimitive)136    void setIsPrimitive(bool isPrimitive)
137    {
138       isPrimitive_ = isPrimitive;
139    }
140 
formals() const141    const std::vector<FormalInformation>& formals() const
142    {
143       return formals_;
144    }
145 
formals()146    std::vector<FormalInformation>& formals()
147    {
148       return formals_;
149    }
150 
151 
getFormalNames() const152    const std::vector<std::string>& getFormalNames() const
153    {
154       return formalNames_;
155    }
156 
defaultValueForFormal(const std::string & formalName)157    const boost::optional<std::string>& defaultValueForFormal(
158          const std::string& formalName)
159    {
160       return infoForFormal(formalName).defaultValue();
161    }
162 
infoForFormal(const std::string & formalName)163    FormalInformation& infoForFormal(const std::string& formalName)
164    {
165       std::size_t n = formals_.size();
166       for (std::size_t i = 0; i < n; ++i)
167          if (formals_[i].name() == formalName)
168             return formals_[i];
169 
170       LOG_WARNING_MESSAGE("No such formal '" + formalName + "'");
171       return noSuchFormal_;
172    }
173 
binding() const174    const boost::optional<Binding>& binding() const
175    {
176       return binding_;
177    }
178 
setPerformsNse(bool performsNse)179    void setPerformsNse(bool performsNse) { performsNse_ = performsNse; }
performsNse() const180    boost::tribool performsNse() const { return performsNse_; }
181 
182 private:
183    boost::optional<Binding> binding_;
184    std::vector<FormalInformation> formals_;
185    std::vector<std::string> formalNames_;
186    boost::tribool isPrimitive_;
187    boost::tribool performsNse_;
188 
189    // Provided so that 'infoForFormal' can return by reference
190    FormalInformation noSuchFormal_;
191 };
192 
193 typedef std::string FunctionName;
194 typedef std::map<FunctionName, FunctionInformation> FunctionInformationMap;
195 
196 struct PackageInformation
197 {
198    std::string package;
199    std::vector<std::string> exports;
200    std::vector<int> types;
201    std::vector<std::string> datasets;
202    FunctionInformationMap functionInfo;
203 };
204 
infoToFormalMap(const std::map<std::string,FunctionInformation> & info)205 inline std::map<std::string, std::vector<std::string> > infoToFormalMap(
206       const std::map<std::string, FunctionInformation>& info)
207 {
208    std::map<std::string, std::vector<std::string> > result;
209    typedef std::map<std::string, FunctionInformation>::const_iterator const_iterator;
210    for (const_iterator it = info.begin(); it != info.end(); ++it)
211       result[it->first] = it->second.getFormalNames();
212    return result;
213 }
214 
215 } // namespace r_util
216 } // namespace core
217 } // namespace rstudio
218 
219 #endif // CORE_R_UTIL_R_FUNCTION_INFORMATION_HPP
220