~ubuntu-branches/debian/sid/kdevelop/sid

« back to all changes in this revision

Viewing changes to languages/cpp/codegen/makeimplementationprivate.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jeremy Lainé
  • Date: 2010-05-05 07:21:55 UTC
  • mfrom: (1.2.3 upstream) (5.1.2 squeeze)
  • Revision ID: james.westby@ubuntu.com-20100505072155-h78lx19pu04sbhtn
Tags: 4:4.0.0-2
* Upload to unstable (Closes: #579947, #481832).
* Acknowledge obsolete NMU fixes (Closes: #562410, #546961).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
   Copyright 2009 Ramón Zarazúa <killerfox512+kde@gmail.com>
 
3
 
 
4
   This library is free software; you can redistribute it and/or
 
5
   modify it under the terms of the GNU Library General Public
 
6
   License version 2 as published by the Free Software Foundation.
 
7
 
 
8
   This library is distributed in the hope that it will be useful,
 
9
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
11
   Library General Public License for more details.
 
12
 
 
13
   You should have received a copy of the GNU Library General Public License
 
14
   along with this library; see the file COPYING.LIB.  If not, write to
 
15
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
16
   Boston, MA 02110-1301, USA.
 
17
*/
 
18
 
 
19
#include "makeimplementationprivate.h"
 
20
#include "ui_privateimplementation.h"
 
21
 
 
22
#include "cppduchain/sourcemanipulation.h"
 
23
#include "cppnewclass.h"
 
24
#include <cppeditorintegrator.h>
 
25
 
 
26
#include <language/duchain/ducontext.h>
 
27
#include <language/duchain/declarationdata.h>
 
28
#include <language/duchain/classmemberdeclaration.h>
 
29
#include <language/duchain/classfunctiondeclaration.h>
 
30
#include <language/duchain/types/functiontype.h>
 
31
#include <language/duchain/types/structuretype.h>
 
32
#include <language/codegen/utilities.h>
 
33
#include <language/codegen/documentchangeset.h>
 
34
#include <language/codegen/coderepresentation.h>
 
35
 
 
36
 
 
37
#include <interfaces/icore.h>
 
38
#include <interfaces/iuicontroller.h>
 
39
 
 
40
#include <kinputdialog.h>
 
41
#include <kparts/mainwindow.h>
 
42
#include <ktexteditor/smartrange.h>
 
43
#include <KMessageBox>
 
44
#include <ast.h>
 
45
#include <astutilities.h>
 
46
#include <language/duchain/functiondefinition.h>
 
47
 
 
48
namespace KDevelop
 
49
{
 
50
 
 
51
/**
 
52
 * @param objectToAllocate The declaration of the object to allocate memory for
 
53
 * @return the stringirized form of the user's preferred method of mamory allocation
 
54
 */
 
55
QString insertMemoryAllocation(const KDevelop::Declaration & objectToAllocate)
 
56
{
 
57
  return "new " + objectToAllocate.toString();
 
58
}
 
59
 
 
60
QString insertMemoryDeallocation(const KDevelop::Declaration& objectToDeallocate)
 
61
{
 
62
  return "delete " + objectToDeallocate.toString();
 
63
}
 
64
 
 
65
bool MakeImplementationPrivate::process()
 
66
{
 
67
    //If invoked through auto generation, then gatherPrivateMembers wan't called
 
68
    if(autoGeneration())
 
69
        gatherPrivateMembers();
 
70
    
 
71
    //Create container for private implementation
 
72
    CppNewClass classGenerator;
 
73
    classGenerator.setType(m_policies.testFlag(ContainerIsClass) ? CppNewClass::Class : CppNewClass::Struct);
 
74
    addDeclarationsToPrivate(classGenerator);
 
75
    classGenerator.identifier(m_structureName);
 
76
    
 
77
    IndexedString implementationFile = CodeGenUtils::fetchImplementationFileForClass(*m_classDeclaration);
 
78
    classGenerator.setHeaderUrl(implementationFile.str());
 
79
    //Set the best matching position before the first use
 
80
    //classGenerator.setHeaderPosition()
 
81
    DocumentChangeSet classChange = classGenerator.generateHeader();
 
82
    classChange.setReplacementPolicy(DocumentChangeSet::StopOnFailedChange);
 
83
    
 
84
    //Temporarily apply the new class into a separate temp file so we can get a chain for it, then merge it
 
85
    classChange.applyToTemp(implementationFile);
 
86
    KDevelop::ReferencedTopDUContext generatedClass = DUChain::self()->waitForUpdate(classChange.tempNameForFile(implementationFile), TopDUContext::AllDeclarationsContextsUsesAndAST);
 
87
    documentChangeSet() << classChange;
 
88
    
 
89
    //Create private implementation pointer member in the class
 
90
    DUChainReadLocker lock(DUChain::lock());
 
91
    SourceCodeInsertion pointerInsertion(m_classContext->topContext());
 
92
    pointerInsertion.setContext(m_classContext);
 
93
    pointerInsertion.setAccess(KDevelop::Declaration::Private);
 
94
    PointerType::Ptr pointer(new PointerType);
 
95
    pointer->setBaseType(AbstractType::Ptr::staticCast<StructureType>(classGenerator.objectType()));
 
96
    pointer->setModifiers(PointerType::ConstModifier);
 
97
    pointerInsertion.insertVariableDeclaration(Identifier(m_privatePointerName), AbstractType::Ptr::dynamicCast<PointerType>(pointer));
 
98
    
 
99
    //Temporarily apply the pointer insertion so that more changes can be made
 
100
    addChangeSet(pointerInsertion.changes());
 
101
    
 
102
    //Add private implementation struct forward declaration before the class
 
103
    Cpp::SourceCodeInsertion forwardDeclare(m_classContext->topContext());
 
104
    forwardDeclare.setInsertBefore(m_classDeclaration->range().start);
 
105
    kDebug() << "Looking for declaration of private class";
 
106
    QList<Declaration *> decls = generatedClass->findDeclarations(Identifier(classGenerator.identifier()));
 
107
    kDebug() << "Found: ";
 
108
    foreach(Declaration * decl, decls)
 
109
        kDebug() << decl->toString();
 
110
    
 
111
    if(!decls.empty())
 
112
    {
 
113
        forwardDeclare.insertForwardDeclaration(decls[0]);
 
114
        
 
115
        lock.unlock();
 
116
        updateConstructors(*decls[0]);
 
117
        updateDestructor();
 
118
    }
 
119
    addChangeSet(forwardDeclare.changes());
 
120
    
 
121
    //Gather all Uses of this class' members
 
122
    lock.lock();
 
123
    UseList allUses;
 
124
    foreach(ClassMemberDeclaration * declaration, m_members)
 
125
    {
 
126
        if(!declaration->type<FunctionType>())
 
127
            allUses[declaration] = declaration->uses();
 
128
    }
 
129
    
 
130
    updateAllUses(allUses);
 
131
    
 
132
    return true;
 
133
}
 
134
 
 
135
namespace
 
136
{
 
137
//TODO Find best place for this convenience function
 
138
bool hasDefaultConstructor(const Declaration * decl)
 
139
{
 
140
    DUContext * context = decl->internalContext();
 
141
    
 
142
    //take into account compiler generated default constructors
 
143
    bool constructorFound = false;
 
144
    
 
145
    foreach(Declaration * member, context->localDeclarations())
 
146
    {
 
147
        if(ClassFunctionDeclaration * classFun = dynamic_cast<ClassFunctionDeclaration *>(member))
 
148
        {
 
149
            TypePtr<FunctionType> funType = classFun->type<FunctionType>();
 
150
            
 
151
            //Check also for all default parameters, counts as default constructor
 
152
            if(classFun->isConstructor())
 
153
            {
 
154
                if(!constructorFound)
 
155
                    constructorFound = true;
 
156
                if(funType && classFun->defaultParametersSize() == funType->indexedArgumentsSize() &&
 
157
                   classFun->internalFunctionContext())
 
158
                    return true;
 
159
            }
 
160
        }
 
161
    }
 
162
  
 
163
    return !constructorFound;
 
164
}
 
165
 
 
166
}
 
167
 
 
168
bool MakeImplementationPrivate::gatherInformation()
 
169
{
 
170
    gatherPrivateMembers();
 
171
    
 
172
    Ui::PrivateImplementationDialog privateDialog;
 
173
    KDialog dialog(KDevelop::ICore::self()->uiController()->activeMainWindow());
 
174
    dialog.setButtons(KDialog::Ok | KDialog::Cancel);
 
175
    dialog.setWindowTitle(i18n("Private Class Implementation Options"));
 
176
    dialog.setInitialSize(QSize(400, 250));
 
177
    
 
178
    privateDialog.setupUi(dialog.mainWidget());
 
179
    
 
180
    CodeGenUtils::IdentifierValidator localValidator(m_classContext);
 
181
    CodeGenUtils::IdentifierValidator globalValidator(m_classContext->topContext());
 
182
    
 
183
    privateDialog.structureName->setValidator(&globalValidator);
 
184
    privateDialog.pointerName->setValidator(&localValidator);
 
185
 
 
186
    DUChainReadLocker lock(DUChain::lock());
 
187
    
 
188
    privateDialog.structureName->setText(m_classContext->scopeIdentifier(true).last().toString() + "Private");
 
189
    
 
190
    //If any of the members is either a reference or has non-default constructor then initialization
 
191
    //must bemoved to the private implementation constructor
 
192
    foreach(ClassMemberDeclaration * declaration, m_members)
 
193
    {
 
194
        AbstractType::Ptr type = declaration->abstractType();
 
195
        if(type->whichType() == AbstractType::TypeReference ||
 
196
           (type->whichType() == AbstractType::TypeStructure && !hasDefaultConstructor(StructureType::Ptr::dynamicCast<AbstractType>(type)->declaration(m_classContext->topContext())) ))
 
197
        {
 
198
            kDebug() << "Forcing private implementation member initialization, because of member: " << declaration->identifier();
 
199
            privateDialog.variableOption->setChecked(true);
 
200
            privateDialog.variableOption->setDisabled(true);
 
201
            break;
 
202
        }
 
203
    }
 
204
    
 
205
    int ret = dialog.exec();
 
206
    
 
207
    if(ret == QDialog::Accepted)
 
208
    { 
 
209
        //Save the names, and options set
 
210
        setPointerName(privateDialog.pointerName->text());
 
211
        setStructureName(privateDialog.structureName->text());
 
212
        
 
213
        m_policies |= (privateDialog.classOption->isChecked() ? ContainerIsClass : EmptyPolicy);
 
214
        m_policies |= (privateDialog.variableOption->isChecked() ? MoveInitializationToPrivate : EmptyPolicy);
 
215
        m_policies |= (privateDialog.methodOption->isChecked() ? MoveMethodsToPrivate : EmptyPolicy);
 
216
        return true;
 
217
    }
 
218
    else
 
219
    {
 
220
        setErrorText("User Abort");
 
221
        return false;
 
222
    }
 
223
}
 
224
 
 
225
bool MakeImplementationPrivate::checkPreconditions(KDevelop::DUContext * context, const KDevelop::DocumentRange &)
 
226
{
 
227
    if(!context)
 
228
    {
 
229
        setErrorText("Could not get the context for text selection");
 
230
        return false;
 
231
    }
 
232
    m_classContext = context;
 
233
    //TODO check that it doesn't already have a private implementation
 
234
    
 
235
    while(m_classContext && m_classContext->type() != DUContext::Class)
 
236
        m_classContext = m_classContext->parentContext();
 
237
    
 
238
    if(!m_classContext)
 
239
    {
 
240
        setErrorText("Selected Context does not belong to a Class");
 
241
        return false;
 
242
    }
 
243
    
 
244
    DUChainReadLocker lock(DUChain::lock());
 
245
    m_classDeclaration = m_classContext->owner();
 
246
    
 
247
    return true;
 
248
}
 
249
 
 
250
void MakeImplementationPrivate::gatherPrivateMembers()
 
251
{
 
252
    DUChainReadLocker lock(DUChain::lock());
 
253
    foreach(Declaration * declaration, m_classContext->localDeclarations())
 
254
    {
 
255
        ClassMemberDeclaration * decl = dynamic_cast<ClassMemberDeclaration *>(declaration);
 
256
        Q_ASSERT(decl);
 
257
        if(decl->accessPolicy() == ClassMemberDeclaration::Private)
 
258
        {
 
259
            if(decl->type<FunctionType>() && !m_policies.testFlag(MoveMethodsToPrivate))
 
260
                continue;
 
261
            m_members << decl;
 
262
        }
 
263
    }
 
264
    
 
265
    kDebug() << "Gathered Privates:";
 
266
#ifndef NDEBUG
 
267
    foreach(ClassMemberDeclaration * decl, m_members)
 
268
      kDebug() << decl->toString();
 
269
#endif
 
270
}
 
271
 
 
272
void MakeImplementationPrivate::updateConstructors(const Declaration & privateStruct)
 
273
{
 
274
    //Gather constructors
 
275
    DUChainReadLocker lock(DUChain::lock());
 
276
    QList<Declaration *> constructors;
 
277
    Declaration * assignmentOp = 0;
 
278
    
 
279
    foreach(Declaration * declaration, m_classContext->localDeclarations())
 
280
    {
 
281
        ClassFunctionDeclaration * fun = dynamic_cast<ClassFunctionDeclaration *>(declaration);
 
282
        if(fun)
 
283
        {
 
284
            //Only gather constructors that have a definition
 
285
            if(fun->isConstructor())
 
286
            {
 
287
                Declaration * def = fun->logicalInternalContext(fun->topContext()) ? fun->logicalInternalContext(fun->topContext())->owner() : 0;
 
288
                if(def)
 
289
                    constructors << def;
 
290
#ifndef NDEBUG                
 
291
                else
 
292
                    kDebug() << "Definition not found for constructor: " << fun->toString();
 
293
#endif
 
294
            }
 
295
            //Gather the definition for the assignment operator
 
296
            else if(!assignmentOp)
 
297
            {
 
298
                QString signature = fun->toString();
 
299
                if(signature.contains("operator=") && fun->type<FunctionType>()->arguments().contains(m_classDeclaration->abstractType()))
 
300
                     assignmentOp = fun->logicalInternalContext(fun->topContext()) ? fun->logicalInternalContext(fun->topContext())->owner() : 0;
 
301
            }
 
302
        }
 
303
    }
 
304
    
 
305
    kDebug() << "Found the following constructors: " << constructors;
 
306
    
 
307
    if(m_policies.testFlag(MoveInitializationToPrivate))
 
308
    {
 
309
        if(constructors.size() > 1)
 
310
            KMessageBox::warningContinueCancel(0, "Warning. It is not recommended to move initialization lists to private constructor when multiple constructors are defined.",
 
311
                                               "PIMPL Generation", KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "PIMPL multiple constructor warning");
 
312
        foreach(Declaration * constructor, constructors)
 
313
        {
 
314
            CodeRepresentation::Ptr rangeRepresentation = representationFor(constructor->url());
 
315
            
 
316
            //Replace the previous constructor 
 
317
            QString privateVersion(constructor->toString());
 
318
            privateVersion.replace(m_classDeclaration->identifier().toString(), m_structureName);
 
319
            
 
320
            documentChangeSet().addChange(DocumentChange(constructor->url(), constructor->range(), constructor->toString(), privateVersion));
 
321
            
 
322
            // Create a "new" version of the previous constructor so that the private one can be called
 
323
            //ParseSession::Ptr astPtr = astContainer(constructor->internalFunctionContext()->url());
 
324
            //documentChangeSet().addChange(DocumentChange(constructor->internalFunctionContext()->url(), SimpleRange(constructor->internalFunctionContext()->range().start, 0),
 
325
                                                        // QString(), constructor->toString() + "\n{\n}\n\n"));
 
326
        }
 
327
    }
 
328
    
 
329
    if(constructors.empty())
 
330
    {
 
331
        //TODO Create a default constructor
 
332
    }
 
333
    
 
334
    QList<IndexedString> filesToUpdate;
 
335
    
 
336
    lock.unlock();
 
337
    foreach(Declaration * constructor, constructors)
 
338
    {
 
339
        
 
340
        //Find the definition of the constructor
 
341
        /*lock.lock();
 
342
        FunctionDefinition * definition = FunctionDefinition::definition(constructor);
 
343
        lock.unlock();*/
 
344
        ParseSession::Ptr astPtr = astContainer(constructor->url());
 
345
        
 
346
        FunctionDefinitionAST * construct = AstUtils::node_cast<FunctionDefinitionAST>(astPtr->astNodeFromDeclaration(constructor));
 
347
        if(construct)
 
348
        {
 
349
            
 
350
            QString insertedText;
 
351
            
 
352
            if(m_policies.testFlag(MoveInitializationToPrivate))
 
353
            {
 
354
                //Send the parameters this constructor takes into the new one
 
355
            }
 
356
            CppEditorIntegrator integrator(astPtr.data());
 
357
            SimpleCursor insertionPoint;
 
358
            
 
359
            //Check for constructors without initializer list
 
360
            if(!construct->constructor_initializers)
 
361
            {
 
362
                insertedText += ":";
 
363
                insertionPoint = integrator.findPosition(construct->function_body->start_token);
 
364
                if(insertionPoint.column > 0)
 
365
                    insertionPoint.column = insertionPoint.column - 1;
 
366
            }
 
367
            else
 
368
              insertionPoint = integrator.findPosition(construct->constructor_initializers->colon);
 
369
            insertedText += " " + m_privatePointerName + "(" + insertMemoryAllocation(privateStruct) + ") ";
 
370
            
 
371
            DocumentChange constructorChange(constructor->url(), SimpleRange(insertionPoint, 0), QString(), insertedText);
 
372
            documentChangeSet().addChange(constructorChange);
 
373
            
 
374
            //Remove the old initializers
 
375
            if(construct->constructor_initializers && construct->constructor_initializers->member_initializers->count())
 
376
            {
 
377
                SimpleRange oldInitializers (integrator.findRange(construct->constructor_initializers->member_initializers->toFront()->element->start_token,
 
378
                                                                  construct->constructor_initializers->member_initializers->toBack()->element->end_token));
 
379
                DocumentChange initializersChange(constructor->url(), oldInitializers, QString(), QString());
 
380
                initializersChange.m_ignoreOldText = true;
 
381
                documentChangeSet().addChange(initializersChange);
 
382
            }
 
383
            if(documentChangeSet().applyToTemp(constructor->url()) && !filesToUpdate.contains(constructor->url()))
 
384
                filesToUpdate << constructor->url();
 
385
        }
 
386
        else
 
387
            kWarning() << "A correct AST node for constructor: " << constructor->toString() << " was not found.";
 
388
    }
 
389
    
 
390
    //TODO Handle assignment operator here as well, and check selection logic
 
391
    foreach(const IndexedString & update, filesToUpdate)
 
392
        DUChain::self()->waitForUpdate(update, static_cast<TopDUContext::Features>(TopDUContext::ForceUpdate | TopDUContext::AllDeclarationsContextsUsesAndAST));
 
393
}
 
394
 
 
395
void MakeImplementationPrivate::updateDestructor()
 
396
{
 
397
    //Find destructor if available
 
398
    ClassFunctionDeclaration * destructor = 0;
 
399
    DUChainReadLocker lock(DUChain::lock());
 
400
    
 
401
    foreach(Declaration * declaration, m_classContext->localDeclarations())
 
402
    {
 
403
        ClassFunctionDeclaration * fun = dynamic_cast<ClassFunctionDeclaration *>(declaration);
 
404
        if(fun && fun->isDestructor())
 
405
        {
 
406
            destructor = fun;
 
407
            break;
 
408
        }
 
409
    }
 
410
   
 
411
    if(!destructor)
 
412
    {
 
413
        SourceCodeInsertion insertion(m_classContext->topContext());
 
414
        insertion.setAccess(KDevelop::Declaration::Public);
 
415
        insertion.setContext(m_classContext);
 
416
        insertion.setInsertBefore(m_classContext->range().end);
 
417
        QString signature("~");
 
418
        signature.append(m_classDeclaration->identifier().toString());
 
419
        
 
420
        ///@todo Allow creation of Destructor body in implementation file
 
421
        ///@todo allow for custom memory deallocation set up by the user
 
422
        QString body = "{\ndelete " + m_privatePointerName + ";\n};";
 
423
        
 
424
        bool result = insertion.insertFunctionDeclaration(Identifier(signature), AbstractType::Ptr(),
 
425
                                                          QList<SourceCodeInsertion::SignatureItem>(), false, body);
 
426
        Q_ASSERT(result);
 
427
        documentChangeSet() << insertion.changes();
 
428
    } 
 
429
    else
 
430
    {
 
431
        DUContext * internal = destructor->logicalInternalContext(destructor->topContext());
 
432
        SimpleCursor inside(internal->range().end);
 
433
        if(inside.column > 0)
 
434
            inside.column = inside.column - 1;
 
435
        DocumentChange destructorChange(internal->url(), SimpleRange(inside, 0),
 
436
                                        QString(), "delete this->" + m_privatePointerName + ";\n");
 
437
    
 
438
        documentChangeSet().addChange(destructorChange);
 
439
    }
 
440
}
 
441
 
 
442
void MakeImplementationPrivate::updateAllUses(UseList & allUses)
 
443
{
 
444
    //For all uses gathered from all members change to access through pointer
 
445
    for(UseList::iterator it = allUses.begin();
 
446
        it != allUses.end(); ++it)
 
447
    {
 
448
        //! @todo check properly if the pointer is being hidden, and add this-> only if necessary
 
449
        QString accessString = it.key()->kind() == Declaration::Instance ? m_privatePointerName + "->" : m_structureName + "::";
 
450
        
 
451
        for(QMap<IndexedString, QList<SimpleRange> >::iterator mapIt = it->begin();
 
452
            mapIt != it->end(); ++mapIt)
 
453
        {
 
454
            kDebug() << "In file: " << mapIt.key().str();
 
455
            //If there is a temporary of this file, then ignore this file, and update the temporary uses
 
456
            if(documentChangeSet().tempNameForFile(mapIt.key()) == mapIt.key())
 
457
                foreach(SimpleRange range, *mapIt)
 
458
                {
 
459
                    CodeRepresentation::Ptr rangeRepresentation = representationFor(mapIt.key());
 
460
                    QString use = rangeRepresentation->rangeText(range.textRange());
 
461
                    kDebug() << "Found use: " << use << "at: " << range.textRange();
 
462
                    DocumentChange useChange(mapIt.key(), range, use, accessString + use);
 
463
                    
 
464
                    Q_ASSERT(documentChangeSet().addChange(useChange));
 
465
                }
 
466
        }
 
467
    }
 
468
}
 
469
 
 
470
CodeRepresentation::Ptr MakeImplementationPrivate::representationFor(IndexedString url)
 
471
{
 
472
    if(!m_representations.contains(url))
 
473
        m_representations[url] = createCodeRepresentation(url);
 
474
    
 
475
    return m_representations[url];
 
476
}
 
477
 
 
478
void MakeImplementationPrivate::addDeclarationsToPrivate(CppNewClass & classGenerator)
 
479
{
 
480
    ParseSession::Ptr ast = astContainer(m_classContext->url());
 
481
    CppEditorIntegrator integrator(ast.data());
 
482
    
 
483
    foreach(Declaration * decl, m_members)
 
484
    {
 
485
        classGenerator.addDeclaration(DeclarationPointer(decl));
 
486
        //Get the context that properly encapsulates the declaration through the AST
 
487
        AST * node = ast->astNodeFromDeclaration(decl);
 
488
        if(node)
 
489
        {
 
490
            SimpleRange declarationRange = integrator.findRange(node);
 
491
            kDebug() << "Found AST node for declaration: " << decl->toString() << ". With range: " << declarationRange.textRange();
 
492
            
 
493
            DocumentChange removeDeclarations(decl->url(), declarationRange, decl->toString(), QString());
 
494
            //Verifying the old text might cause conflicts with variables defined after structure declarations
 
495
            removeDeclarations.m_ignoreOldText = true;
 
496
            documentChangeSet().addChange(removeDeclarations);
 
497
        }
 
498
        else
 
499
            kWarning() << "Did not find an AST node mapped for declarationn: " << decl->toString();
 
500
    }
 
501
    
 
502
    //Remove all the declarations now, so they don't interfere later
 
503
    documentChangeSet().applyToTemp(m_classDeclaration->url());
 
504
}
 
505
 
 
506
}