File: dynamiclanguageexpressionvisitor.h

package info (click to toggle)
kdevelop 4%3A5.6.2-4
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 57,892 kB
  • sloc: cpp: 278,773; javascript: 3,558; python: 3,385; sh: 1,317; ansic: 689; xml: 273; php: 95; makefile: 40; lisp: 13; sed: 12
file content (220 lines) | stat: -rw-r--r-- 8,290 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/***************************************************************************
*   This file is part of KDevelop                                         *
*   Copyright 2014 Sven Brauch <svenbrauch@gmail.com>                     *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU Library General Public License as       *
*   published by the Free Software Foundation; either version 2 of the    *
*   License, or (at your option) any later version.                       *
*                                                                         *
*   This program is distributed in the hope that it will be useful,       *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
*   GNU General Public License for more details.                          *
*                                                                         *
*   You should have received a copy of the GNU Library General Public     *
*   License along with this program; if not, write to the                 *
*   Free Software Foundation, Inc.,                                       *
*   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
***************************************************************************/

#ifndef KDEVPLATFORM_DYNAMICLANGUAGEEXPRESSIONVISITOR_H
#define KDEVPLATFORM_DYNAMICLANGUAGEEXPRESSIONVISITOR_H

#include "language/duchain/ducontext.h"
#include <language/languageexport.h>

namespace KDevelop {
/**
 * @brief Provides functionality commonly needed for expression visitors in dynamically typed languages.
 *
 * In languages such as Python, Ruby, PHP and JS an expression visitor is commonly used
 * to find the type of an expression, such as "3", "3+5", "a.b" or "func(arg)".
 * Since that requires almost the same logic, it is also commonly used to find out
 * the declaration of which a type should be updated in -- for example -- an assignment.
 *
 * Your expression visitor should inherit your default AST visitor as well as this class.
 *
 * Consider this Python code as an example:
 * \code
 *   a = 3
 *   b = str(a)
 * \endcode
 * This results in an AST roughly like this:
 * \code
 *   Module [
 *     Assignment {
 *       lhs = Name("a"),
 *       rhs = IntegerLiteral
 *     },
 *     Assignment {
 *       lhs = Name("b"),
 *       rhs = Call {
 *         func = "str",
 *         args = Arguments[ Name("a") ]
 *       }
 *     }
 *   ]
 * \endcode
 * In your expression visitor, you would implement
 *   - visitIntegerLiteral to set the last type to int, e.g. by calling
 *     \code
 *       encounter(AbstractType::Ptr(new IntegralType(IntegralType::TypeInt)))
 *     \endcode
 *     from there
 *   - visitCall to set the last type to the function's return type,
 *     by finding the function which is called in the duchain and calling
 *     encounter() on its return type as above.
 *
 * For parsing either assignment, you could then
 *   - in the declaration builder, create an ExpressionVisitor
 *   - call that visitor's visit() method on the assignment's rhs
 *     (this method is inherited from your language's default visitor)
 *   - set or update the type of the Name on the left side to
 *     the visitor's lastType().
 *
 * A example for a more complex case would be this:
 * \code
 *   a = list()
 *   a[0] = 4
 * \endcode
 * Here, when dealing with the second assignment, in the declaration builder
 * you want to know the declaration of which you have to update the content type.
 * Thus, in your expression visitor, when you visit an index access, you can provide
 * a declaration to encounter() which can later be retrieved using lastDeclaration().
 * This makes it relatively straightforward to handle arbitrarily complex situations such as
 * \code
 *   a.b[0].c[3] = 42
 * \endcode
 * with little to no extra effort.
 */
class KDEVPLATFORMLANGUAGE_EXPORT DynamicLanguageExpressionVisitor
{
public:
    /**
     * @brief Construct a new expression visitor in the given @p context.
     *
     * @param context The DUContext the expression visitor resolves names in.
     */
    explicit DynamicLanguageExpressionVisitor(const DUContext* context);

    /**
     * @brief Construct a new expression visitor and copy all fixed properties from @p parent.
     */
    explicit DynamicLanguageExpressionVisitor(DynamicLanguageExpressionVisitor* parent);

    virtual ~DynamicLanguageExpressionVisitor() { };

    /**
     * @brief Return the DUContext this visitor is working on.
     */
    inline const DUContext* context() const
    {
        return m_context;
    }

    inline const TopDUContext* topContext() const
    {
        return m_context->topContext();
    }

    /**
     * @brief Retrieve this visitor's last encountered type.
     * This is never a null type.
     */
    inline AbstractType::Ptr lastType() const
    {
        if (!m_lastType) {
            return unknownType();
        }
        return m_lastType;
    }

    /**
     * @brief Retrieve this visitor's last encountered declaration. May be null.
     */
    inline DeclarationPointer lastDeclaration() const
    {
        return m_lastDeclaration;
    }

    /**
     * @brief Check whether this visitor thinks its computed result is reliable or not.
     * This can be used to give hints to other builders about whether a problem should be
     * reported to the user or not in some situations. For example, if a member of a variable
     * is accessed which does not seem to exist, you can create a problem if the expression
     * visitor which determined the type of the variable is confident of what it found, and
     * otherwise do nothing.
     */
    inline bool isConfident() const
    {
        return m_isConfident;
    }

    /**
     * @brief Encounter @p lvalueDeclaration as an lvalue.
     * Calling this function sets the last declaration to @p lvalueDeclaration and the
     * last type to @p lvalueDeclaration 's type.
     * it is intended to be used for e.g. attribute access:
     * \code
     *     a.b = 3
     * \endcode
     * When visiting the AST of the "a.b" expression,
     * the expression visitor should call encounterLvalue on
     * the declaration of the "b" property of "a".
     *
     * Calling this function with decl as @p lvalueDeclaration is the same
     * as calling encounter(decl->abstractType(), decl).
     *
     * @param lvalueDeclaration The declaration to set as last declaration and derive the last type from
     */
    void encounterLvalue(const DeclarationPointer& lvalueDeclaration);

    /**
     * @brief Encounter the given type and declaration.
     * If @p declaration is null, the visitor's last declaration is cleared.
     *
     * @param type Type to set as the last type
     * @param declaration Declaration to set as the last declaration; null by default
     */
    void encounter(const AbstractType::Ptr& type, const DeclarationPointer& declaration = DeclarationPointer());

    /**
     * @brief Set the last type to unknownType() and clear the last declaration.
     */
    void encounterUnknown();

    /**
     * @brief Should return the type to use when no type is known.
     * The default implementation returns IntegralType::TypeMixed.
     * @warning You must not return a null type from this function.
     */
    virtual AbstractType::Ptr unknownType() const;

protected:
    /**
     * @see isConfident
     */
    inline void setConfident(bool confident)
    {
        m_isConfident = confident;
    }

    /**
     * @brief Reimplement this if you need a hook before a type is encountered.
     * Default implementation does nothing.
     * @param type the type which would be encountered
     * @return the type which will be encountered instead
     */
    virtual AbstractType::Ptr encounterPreprocess(AbstractType::Ptr type);

protected:
    const DUContext* m_context;
    AbstractType::Ptr m_lastType;
    DeclarationPointer m_lastDeclaration;
    bool m_isConfident = true;
    DynamicLanguageExpressionVisitor* m_parentVisitor;
};
} // namespace KDevelop

#endif // KDEVPLATFORM_DYNAMICLANGUAGEEXPRESSIONVISITOR_H