~ubuntu-branches/debian/stretch/gnome-builder/stretch

« back to all changes in this revision

Viewing changes to libide/clang/ide-clang-translation-unit.c

  • Committer: Package Import Robot
  • Author(s): Andreas Henriksson
  • Date: 2015-10-11 12:38:45 UTC
  • Revision ID: package-import@ubuntu.com-20151011123845-a0hvkz01se0p1p5a
Tags: upstream-3.16.3
ImportĀ upstreamĀ versionĀ 3.16.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ide-clang-translation-unit.c
 
2
 *
 
3
 * Copyright (C) 2015 Christian Hergert <christian@hergert.me>
 
4
 *
 
5
 * This program is free software: you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation, either version 3 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
 */
 
18
 
 
19
#define G_LOG_DOMAIN "clang-translation-unit"
 
20
 
 
21
#include <clang-c/Index.h>
 
22
#include <glib/gi18n.h>
 
23
 
 
24
#include "egg-counter.h"
 
25
 
 
26
#include "ide-context.h"
 
27
#include "ide-clang-completion-item.h"
 
28
#include "ide-clang-private.h"
 
29
#include "ide-clang-translation-unit.h"
 
30
#include "ide-debug.h"
 
31
#include "ide-diagnostic.h"
 
32
#include "ide-diagnostics.h"
 
33
#include "ide-file.h"
 
34
#include "ide-internal.h"
 
35
#include "ide-project.h"
 
36
#include "ide-ref-ptr.h"
 
37
#include "ide-source-location.h"
 
38
#include "ide-symbol.h"
 
39
#include "ide-thread-pool.h"
 
40
#include "ide-unsaved-file.h"
 
41
#include "ide-unsaved-files.h"
 
42
#include "ide-vcs.h"
 
43
 
 
44
struct _IdeClangTranslationUnit
 
45
{
 
46
  IdeObject          parent_instance;
 
47
 
 
48
  CXTranslationUnit  tu;
 
49
  gint64             serial;
 
50
  GFile             *file;
 
51
  IdeHighlightIndex *index;
 
52
  GHashTable        *diagnostics;
 
53
};
 
54
 
 
55
typedef struct
 
56
{
 
57
  GPtrArray *unsaved_files;
 
58
  gchar     *path;
 
59
  guint      line;
 
60
  guint      line_offset;
 
61
} CodeCompleteState;
 
62
 
 
63
typedef struct
 
64
{
 
65
  GPtrArray *ar;
 
66
  IdeFile   *file;
 
67
  gchar     *path;
 
68
} GetSymbolsState;
 
69
 
 
70
G_DEFINE_TYPE (IdeClangTranslationUnit, ide_clang_translation_unit, IDE_TYPE_OBJECT)
 
71
EGG_DEFINE_COUNTER (instances, "Clang", "Translation Units", "Number of clang translation units")
 
72
 
 
73
enum {
 
74
  PROP_0,
 
75
  PROP_FILE,
 
76
  PROP_INDEX,
 
77
  PROP_NATIVE,
 
78
  PROP_SERIAL,
 
79
  LAST_PROP
 
80
};
 
81
 
 
82
static GParamSpec *gParamSpecs [LAST_PROP];
 
83
 
 
84
static void
 
85
code_complete_state_free (gpointer data)
 
86
{
 
87
  CodeCompleteState *state = data;
 
88
 
 
89
  if (state)
 
90
    {
 
91
      g_clear_pointer (&state->unsaved_files, g_ptr_array_unref);
 
92
      g_free (state->path);
 
93
      g_free (state);
 
94
    }
 
95
}
 
96
 
 
97
/**
 
98
 * ide_clang_translation_unit_get_index:
 
99
 * @self: A #IdeClangTranslationUnit.
 
100
 *
 
101
 * Gets the highlight index for the translation unit.
 
102
 *
 
103
 * Returns: (transfer none) (nullable): An #IdeHighlightIndex or %NULL.
 
104
 */
 
105
IdeHighlightIndex *
 
106
ide_clang_translation_unit_get_index (IdeClangTranslationUnit *self)
 
107
{
 
108
  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
 
109
 
 
110
  return self->index;
 
111
}
 
112
 
 
113
static void
 
114
ide_clang_translation_unit_set_index (IdeClangTranslationUnit *self,
 
115
                                      IdeHighlightIndex       *index)
 
116
{
 
117
  g_assert (IDE_IS_CLANG_TRANSLATION_UNIT (self));
 
118
 
 
119
  if (index != NULL)
 
120
    self->index = ide_highlight_index_ref (index);
 
121
}
 
122
 
 
123
GFile *
 
124
ide_clang_translation_unit_get_file (IdeClangTranslationUnit *self)
 
125
{
 
126
  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
 
127
 
 
128
  return self->file;
 
129
}
 
130
 
 
131
static void
 
132
ide_clang_translation_unit_set_file (IdeClangTranslationUnit *self,
 
133
                                     GFile                   *file)
 
134
{
 
135
  g_return_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self));
 
136
  g_return_if_fail (G_IS_FILE (file));
 
137
 
 
138
  if (g_set_object (&self->file, file))
 
139
    g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_FILE]);
 
140
}
 
141
 
 
142
IdeClangTranslationUnit *
 
143
_ide_clang_translation_unit_new (IdeContext        *context,
 
144
                                 CXTranslationUnit  tu,
 
145
                                 GFile             *file,
 
146
                                 IdeHighlightIndex *index,
 
147
                                 gint64             serial)
 
148
{
 
149
  IdeClangTranslationUnit *ret;
 
150
 
 
151
  g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
 
152
  g_return_val_if_fail (tu, NULL);
 
153
  g_return_val_if_fail (!file || G_IS_FILE (file), NULL);
 
154
 
 
155
  ret = g_object_new (IDE_TYPE_CLANG_TRANSLATION_UNIT,
 
156
                      "context", context,
 
157
                      "file", file,
 
158
                      "index", index,
 
159
                      "native", tu,
 
160
                      "serial", serial,
 
161
                      NULL);
 
162
 
 
163
  return ret;
 
164
}
 
165
 
 
166
static IdeDiagnosticSeverity
 
167
translate_severity (enum CXDiagnosticSeverity severity)
 
168
{
 
169
  switch (severity)
 
170
    {
 
171
    case CXDiagnostic_Ignored:
 
172
      return IDE_DIAGNOSTIC_IGNORED;
 
173
 
 
174
    case CXDiagnostic_Note:
 
175
      return IDE_DIAGNOSTIC_NOTE;
 
176
 
 
177
    case CXDiagnostic_Warning:
 
178
      return IDE_DIAGNOSTIC_WARNING;
 
179
 
 
180
    case CXDiagnostic_Error:
 
181
      return IDE_DIAGNOSTIC_ERROR;
 
182
 
 
183
    case CXDiagnostic_Fatal:
 
184
      return IDE_DIAGNOSTIC_FATAL;
 
185
 
 
186
    default:
 
187
      return 0;
 
188
    }
 
189
}
 
190
 
 
191
static gchar *
 
192
get_path (const gchar *workpath,
 
193
          const gchar *path)
 
194
{
 
195
  if (g_str_has_prefix (path, workpath))
 
196
    {
 
197
      path = path + strlen (workpath);
 
198
      while (*path == G_DIR_SEPARATOR)
 
199
        path++;
 
200
 
 
201
      return g_strdup (path);
 
202
    }
 
203
 
 
204
  return g_strdup (path);
 
205
}
 
206
 
 
207
static IdeSourceLocation *
 
208
create_location (IdeClangTranslationUnit *self,
 
209
                 IdeProject              *project,
 
210
                 const gchar             *workpath,
 
211
                 CXSourceLocation         cxloc)
 
212
{
 
213
  IdeSourceLocation *ret = NULL;
 
214
  IdeFile *file = NULL;
 
215
  CXFile cxfile = NULL;
 
216
  g_autofree gchar *path = NULL;
 
217
  const gchar *cstr;
 
218
  CXString str;
 
219
  unsigned line;
 
220
  unsigned column;
 
221
  unsigned offset;
 
222
 
 
223
  g_return_val_if_fail (self, NULL);
 
224
  g_return_val_if_fail (workpath, NULL);
 
225
 
 
226
  clang_getFileLocation (cxloc, &cxfile, &line, &column, &offset);
 
227
 
 
228
  if (line > 0) line--;
 
229
  if (column > 0) column--;
 
230
 
 
231
  str = clang_getFileName (cxfile);
 
232
  cstr = clang_getCString (str);
 
233
  if (cstr != NULL)
 
234
    path = get_path (workpath, cstr);
 
235
  clang_disposeString (str);
 
236
  if (cstr == NULL)
 
237
    return NULL;
 
238
 
 
239
  file = ide_project_get_file_for_path (project, path);
 
240
 
 
241
  if (!file)
 
242
    {
 
243
      IdeContext *context;
 
244
      GFile *gfile;
 
245
 
 
246
      context = ide_object_get_context (IDE_OBJECT (self));
 
247
      gfile = g_file_new_for_path (path);
 
248
 
 
249
      file = g_object_new (IDE_TYPE_FILE,
 
250
                           "context", context,
 
251
                           "file", gfile,
 
252
                           "path", path,
 
253
                           NULL);
 
254
    }
 
255
 
 
256
  ret = ide_source_location_new (file, line, column, offset);
 
257
 
 
258
  return ret;
 
259
}
 
260
 
 
261
static IdeSourceRange *
 
262
create_range (IdeClangTranslationUnit *self,
 
263
              IdeProject              *project,
 
264
              const gchar             *workpath,
 
265
              CXSourceRange            cxrange)
 
266
{
 
267
  IdeSourceRange *range = NULL;
 
268
  CXSourceLocation cxbegin;
 
269
  CXSourceLocation cxend;
 
270
  g_autoptr(IdeSourceLocation) begin = NULL;
 
271
  g_autoptr(IdeSourceLocation) end = NULL;
 
272
 
 
273
  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
 
274
 
 
275
  cxbegin = clang_getRangeStart (cxrange);
 
276
  cxend = clang_getRangeEnd (cxrange);
 
277
 
 
278
  begin = create_location (self, project, workpath, cxbegin);
 
279
  end = create_location (self, project, workpath, cxend);
 
280
 
 
281
  if ((begin != NULL) && (end != NULL))
 
282
    range = _ide_source_range_new (begin, end);
 
283
 
 
284
  return range;
 
285
}
 
286
 
 
287
static gboolean
 
288
cxfile_equal (CXFile  cxfile,
 
289
              GFile  *file)
 
290
{
 
291
  CXString cxstr;
 
292
  gchar *path;
 
293
  gboolean ret;
 
294
 
 
295
  cxstr = clang_getFileName (cxfile);
 
296
  path = g_file_get_path (file);
 
297
 
 
298
  ret = (0 == g_strcmp0 (clang_getCString (cxstr), path));
 
299
 
 
300
  clang_disposeString (cxstr);
 
301
  g_free (path);
 
302
 
 
303
  return ret;
 
304
}
 
305
 
 
306
static IdeDiagnostic *
 
307
create_diagnostic (IdeClangTranslationUnit *self,
 
308
                   IdeProject              *project,
 
309
                   const gchar             *workpath,
 
310
                   GFile                   *target,
 
311
                   CXDiagnostic            *cxdiag)
 
312
{
 
313
  enum CXDiagnosticSeverity cxseverity;
 
314
  IdeDiagnosticSeverity severity;
 
315
  IdeDiagnostic *diag;
 
316
  IdeSourceLocation *loc;
 
317
  g_autofree gchar *spelling = NULL;
 
318
  CXString cxstr;
 
319
  CXSourceLocation cxloc;
 
320
  CXFile cxfile = NULL;
 
321
  guint num_ranges;
 
322
  guint i;
 
323
 
 
324
  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
 
325
  g_return_val_if_fail (cxdiag, NULL);
 
326
 
 
327
  cxloc = clang_getDiagnosticLocation (cxdiag);
 
328
  clang_getExpansionLocation (cxloc, &cxfile, NULL, NULL, NULL);
 
329
 
 
330
  if (cxfile && !cxfile_equal (cxfile, target))
 
331
    return NULL;
 
332
 
 
333
  cxseverity = clang_getDiagnosticSeverity (cxdiag);
 
334
  severity = translate_severity (cxseverity);
 
335
 
 
336
  cxstr = clang_getDiagnosticSpelling (cxdiag);
 
337
  spelling = g_strdup (clang_getCString (cxstr));
 
338
  clang_disposeString (cxstr);
 
339
 
 
340
  /*
 
341
   * I thought we could use an approach like the following to get deprecation
 
342
   * status. However, it has so far proven ineffective.
 
343
   *
 
344
   *   cursor = clang_getCursor (self->tu, cxloc);
 
345
   *   avail = clang_getCursorAvailability (cursor);
 
346
   */
 
347
  if ((severity == IDE_DIAGNOSTIC_WARNING) &&
 
348
      (spelling != NULL) &&
 
349
      (strstr (spelling, "deprecated") != NULL))
 
350
    severity = IDE_DIAGNOSTIC_DEPRECATED;
 
351
 
 
352
  loc = create_location (self, project, workpath, cxloc);
 
353
 
 
354
  diag = _ide_diagnostic_new (severity, spelling, loc);
 
355
 
 
356
  num_ranges = clang_getDiagnosticNumRanges (cxdiag);
 
357
 
 
358
  for (i = 0; i < num_ranges; i++)
 
359
    {
 
360
      CXSourceRange cxrange;
 
361
      IdeSourceRange *range;
 
362
 
 
363
      cxrange = clang_getDiagnosticRange (cxdiag, i);
 
364
      range = create_range (self, project, workpath, cxrange);
 
365
      if (range != NULL)
 
366
        _ide_diagnostic_take_range (diag, range);
 
367
    }
 
368
 
 
369
  return diag;
 
370
}
 
371
 
 
372
/**
 
373
 * ide_clang_translation_unit_get_diagnostics_for_file:
 
374
 *
 
375
 * Retrieves the diagnostics for the translation unit for a specific file.
 
376
 *
 
377
 * Returns: (transfer none) (nullable): An #IdeDiagnostics or %NULL.
 
378
 */
 
379
IdeDiagnostics *
 
380
ide_clang_translation_unit_get_diagnostics_for_file (IdeClangTranslationUnit *self,
 
381
                                                     GFile                   *file)
 
382
{
 
383
  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
 
384
 
 
385
  if (!g_hash_table_contains (self->diagnostics, file))
 
386
    {
 
387
      IdeContext *context;
 
388
      IdeProject *project;
 
389
      IdeVcs *vcs;
 
390
      g_autofree gchar *workpath = NULL;
 
391
      GFile *workdir;
 
392
      GPtrArray *diags;
 
393
      guint count;
 
394
      guint i;
 
395
 
 
396
      diags = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_diagnostic_unref);
 
397
 
 
398
      /*
 
399
       * Acquire the reader lock for the project since we will need to do
 
400
       * a bunch of project tree lookups when creating diagnostics. By doing
 
401
       * this outside of the loops, we avoid creating lots of contention on
 
402
       * the reader lock, but potentially hold on to the entire lock for a bit
 
403
       * longer at a time.
 
404
       */
 
405
      context = ide_object_get_context (IDE_OBJECT (self));
 
406
      project = ide_context_get_project (context);
 
407
      vcs = ide_context_get_vcs (context);
 
408
      workdir = ide_vcs_get_working_directory (vcs);
 
409
      workpath = g_file_get_path (workdir);
 
410
 
 
411
      ide_project_reader_lock (project);
 
412
 
 
413
      count = clang_getNumDiagnostics (self->tu);
 
414
      for (i = 0; i < count; i++)
 
415
        {
 
416
          CXDiagnostic cxdiag;
 
417
          IdeDiagnostic *diag;
 
418
 
 
419
          cxdiag = clang_getDiagnostic (self->tu, i);
 
420
          diag = create_diagnostic (self, project, workpath, file, cxdiag);
 
421
 
 
422
          if (diag != NULL)
 
423
            {
 
424
              guint num_fixits;
 
425
              gsize j;
 
426
 
 
427
              num_fixits = clang_getDiagnosticNumFixIts (cxdiag);
 
428
 
 
429
              for (j = 0; j < num_fixits; j++)
 
430
                {
 
431
                  IdeFixit *fixit = NULL;
 
432
                  IdeSourceRange *range;
 
433
                  CXSourceRange cxrange;
 
434
                  CXString cxstr;
 
435
 
 
436
                  cxstr = clang_getDiagnosticFixIt (cxdiag, j, &cxrange);
 
437
                  range = create_range (self, project, workpath, cxrange);
 
438
                  fixit = _ide_fixit_new (range, clang_getCString (cxstr));
 
439
                  clang_disposeString (cxstr);
 
440
 
 
441
                  if (fixit != NULL)
 
442
                    _ide_diagnostic_take_fixit (diag, fixit);
 
443
                }
 
444
 
 
445
              g_ptr_array_add (diags, diag);
 
446
            }
 
447
 
 
448
          clang_disposeDiagnostic (cxdiag);
 
449
        }
 
450
 
 
451
      ide_project_reader_unlock (project);
 
452
 
 
453
      g_hash_table_insert (self->diagnostics, g_object_ref (file), _ide_diagnostics_new (diags));
 
454
    }
 
455
 
 
456
  return g_hash_table_lookup (self->diagnostics, file);
 
457
}
 
458
 
 
459
/**
 
460
 * ide_clang_translation_unit_get_diagnostics:
 
461
 *
 
462
 * Retrieves the diagnostics for the translation unit.
 
463
 *
 
464
 * Returns: (transfer none) (nullable): An #IdeDiagnostics or %NULL.
 
465
 */
 
466
IdeDiagnostics *
 
467
ide_clang_translation_unit_get_diagnostics (IdeClangTranslationUnit *self)
 
468
{
 
469
  return ide_clang_translation_unit_get_diagnostics_for_file (self, self->file);
 
470
}
 
471
 
 
472
gint64
 
473
ide_clang_translation_unit_get_serial (IdeClangTranslationUnit *self)
 
474
{
 
475
  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), -1);
 
476
 
 
477
  return self->serial;
 
478
}
 
479
 
 
480
static void
 
481
ide_clang_translation_unit_finalize (GObject *object)
 
482
{
 
483
  IdeClangTranslationUnit *self = (IdeClangTranslationUnit *)object;
 
484
 
 
485
  IDE_ENTRY;
 
486
 
 
487
  clang_disposeTranslationUnit (self->tu);
 
488
  g_clear_object (&self->file);
 
489
  g_clear_pointer (&self->index, ide_highlight_index_unref);
 
490
  g_clear_pointer (&self->diagnostics, g_hash_table_unref);
 
491
 
 
492
  G_OBJECT_CLASS (ide_clang_translation_unit_parent_class)->finalize (object);
 
493
 
 
494
  EGG_COUNTER_DEC (instances);
 
495
 
 
496
  IDE_EXIT;
 
497
}
 
498
 
 
499
static void
 
500
ide_clang_translation_unit_get_property (GObject    *object,
 
501
                                         guint       prop_id,
 
502
                                         GValue     *value,
 
503
                                         GParamSpec *pspec)
 
504
{
 
505
  IdeClangTranslationUnit *self = IDE_CLANG_TRANSLATION_UNIT (object);
 
506
 
 
507
  switch (prop_id)
 
508
    {
 
509
    case PROP_FILE:
 
510
      g_value_set_object (value, ide_clang_translation_unit_get_file (self));
 
511
      break;
 
512
 
 
513
    case PROP_INDEX:
 
514
      g_value_set_boxed (value, ide_clang_translation_unit_get_index (self));
 
515
      break;
 
516
 
 
517
    case PROP_SERIAL:
 
518
      g_value_set_int64 (value, ide_clang_translation_unit_get_serial (self));
 
519
      break;
 
520
 
 
521
    default:
 
522
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
523
    }
 
524
}
 
525
 
 
526
static void
 
527
ide_clang_translation_unit_set_property (GObject      *object,
 
528
                                         guint         prop_id,
 
529
                                         const GValue *value,
 
530
                                         GParamSpec   *pspec)
 
531
{
 
532
  IdeClangTranslationUnit *self = IDE_CLANG_TRANSLATION_UNIT (object);
 
533
 
 
534
  switch (prop_id)
 
535
    {
 
536
    case PROP_FILE:
 
537
      ide_clang_translation_unit_set_file (self, g_value_get_object (value));
 
538
      break;
 
539
 
 
540
    case PROP_INDEX:
 
541
      ide_clang_translation_unit_set_index (self, g_value_get_boxed (value));
 
542
      break;
 
543
 
 
544
    case PROP_SERIAL:
 
545
      self->serial = g_value_get_int64 (value);
 
546
      break;
 
547
 
 
548
    case PROP_NATIVE:
 
549
      self->tu = g_value_get_pointer (value);
 
550
      break;
 
551
 
 
552
    default:
 
553
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
554
    }
 
555
}
 
556
 
 
557
static void
 
558
ide_clang_translation_unit_class_init (IdeClangTranslationUnitClass *klass)
 
559
{
 
560
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
561
 
 
562
  object_class->finalize = ide_clang_translation_unit_finalize;
 
563
  object_class->get_property = ide_clang_translation_unit_get_property;
 
564
  object_class->set_property = ide_clang_translation_unit_set_property;
 
565
 
 
566
  gParamSpecs [PROP_FILE] =
 
567
    g_param_spec_object ("file",
 
568
                         _("File"),
 
569
                         _("The file used to build the translation unit."),
 
570
                         G_TYPE_FILE,
 
571
                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 
572
 
 
573
  gParamSpecs [PROP_INDEX] =
 
574
    g_param_spec_boxed ("index",
 
575
                         _("Index"),
 
576
                         _("The highlight index for the translation unit."),
 
577
                         IDE_TYPE_HIGHLIGHT_INDEX,
 
578
                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 
579
 
 
580
  gParamSpecs [PROP_NATIVE] =
 
581
    g_param_spec_pointer ("native",
 
582
                          _("Native"),
 
583
                          _("The native translation unit pointer."),
 
584
                          (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 
585
 
 
586
  gParamSpecs [PROP_SERIAL] =
 
587
    g_param_spec_int64 ("serial",
 
588
                        _("Serial"),
 
589
                        _("A sequence number for the translation unit."),
 
590
                        0,
 
591
                        G_MAXINT64,
 
592
                        0,
 
593
                        (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 
594
 
 
595
  g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
 
596
}
 
597
 
 
598
static void
 
599
ide_clang_translation_unit_init (IdeClangTranslationUnit *self)
 
600
{
 
601
  EGG_COUNTER_INC (instances);
 
602
 
 
603
  self->diagnostics = g_hash_table_new_full ((GHashFunc)g_file_hash,
 
604
                                             (GEqualFunc)g_file_equal,
 
605
                                             g_object_unref,
 
606
                                             (GDestroyNotify)ide_diagnostics_unref);
 
607
}
 
608
 
 
609
static void
 
610
ide_clang_translation_unit_code_complete_worker (GTask        *task,
 
611
                                                 gpointer      source_object,
 
612
                                                 gpointer      task_data,
 
613
                                                 GCancellable *cancellable)
 
614
{
 
615
  IdeClangTranslationUnit *self = source_object;
 
616
  CodeCompleteState *state = task_data;
 
617
  CXCodeCompleteResults *results;
 
618
  g_autoptr(IdeRefPtr) refptr = NULL;
 
619
  struct CXUnsavedFile *ufs;
 
620
  g_autoptr(GPtrArray) ar = NULL;
 
621
  gsize i;
 
622
  gsize j = 0;
 
623
 
 
624
  g_assert (IDE_IS_CLANG_TRANSLATION_UNIT (self));
 
625
  g_assert (state);
 
626
  g_assert (state->unsaved_files);
 
627
 
 
628
  /*
 
629
   * FIXME: Not thread safe! We should probably add a "Pending" flag or something that is
 
630
   *        similar to g_input_stream_set_pending().
 
631
   */
 
632
 
 
633
  if (!state->path)
 
634
    {
 
635
      /* implausable to reach here, anyway */
 
636
      g_task_return_new_error (task,
 
637
                               G_IO_ERROR,
 
638
                               G_IO_ERROR_INVALID_FILENAME,
 
639
                               _("clang_codeCompleteAt() only works on local files"));
 
640
      return;
 
641
    }
 
642
 
 
643
  ufs = g_new0 (struct CXUnsavedFile, state->unsaved_files->len);
 
644
 
 
645
  for (i = 0; i < state->unsaved_files->len; i++)
 
646
    {
 
647
      IdeUnsavedFile *uf;
 
648
      gchar *path;
 
649
      GFile *file;
 
650
 
 
651
      uf = g_ptr_array_index (state->unsaved_files, i);
 
652
      file = ide_unsaved_file_get_file (uf);
 
653
      path = g_file_get_path (file);
 
654
 
 
655
      /*
 
656
       * NOTE: Some files might not be local, and therefore return a NULL path.
 
657
       *       Also, we will free the path from the (const char *) pointer after
 
658
       *       executing the work.
 
659
       */
 
660
      if (path != NULL)
 
661
        {
 
662
          GBytes *content = ide_unsaved_file_get_content (uf);
 
663
 
 
664
          ufs [j].Filename = path;
 
665
          ufs [j].Contents = g_bytes_get_data (content, NULL);
 
666
          ufs [j].Length = g_bytes_get_size (content);
 
667
 
 
668
          j++;
 
669
        }
 
670
    }
 
671
 
 
672
  results = clang_codeCompleteAt (self->tu,
 
673
                                  state->path,
 
674
                                  state->line + 1,
 
675
                                  state->line_offset + 1,
 
676
                                  ufs, j,
 
677
                                  clang_defaultCodeCompleteOptions ());
 
678
 
 
679
  /*
 
680
   * encapsulate in refptr so we don't need to malloc lots of little strings.
 
681
   * we will inflate result strings as necessary.
 
682
   */
 
683
  refptr = ide_ref_ptr_new (results, (GDestroyNotify)clang_disposeCodeCompleteResults);
 
684
  ar = g_ptr_array_new ();
 
685
 
 
686
  for (i = 0; i < results->NumResults; i++)
 
687
    {
 
688
      GtkSourceCompletionProposal *proposal;
 
689
 
 
690
      proposal = g_object_new (IDE_TYPE_CLANG_COMPLETION_ITEM,
 
691
                               "results", ide_ref_ptr_ref (refptr),
 
692
                               "index", (guint)i,
 
693
                               NULL);
 
694
      g_ptr_array_add (ar, proposal);
 
695
    }
 
696
 
 
697
  g_task_return_pointer (task, g_ptr_array_ref (ar), (GDestroyNotify)g_ptr_array_unref);
 
698
 
 
699
  /* cleanup malloc'd state */
 
700
  for (i = 0; i < j; i++)
 
701
    g_free ((gchar *)ufs [i].Filename);
 
702
  g_free (ufs);
 
703
}
 
704
 
 
705
 
 
706
void
 
707
ide_clang_translation_unit_code_complete_async (IdeClangTranslationUnit *self,
 
708
                                                GFile                   *file,
 
709
                                                const GtkTextIter       *location,
 
710
                                                GCancellable            *cancellable,
 
711
                                                GAsyncReadyCallback      callback,
 
712
                                                gpointer                 user_data)
 
713
{
 
714
  g_autoptr(GTask) task = NULL;
 
715
  CodeCompleteState *state;
 
716
  IdeContext *context;
 
717
  IdeUnsavedFiles *unsaved_files;
 
718
 
 
719
  IDE_ENTRY;
 
720
 
 
721
  g_return_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self));
 
722
  g_return_if_fail (G_IS_FILE (file));
 
723
  g_return_if_fail (location);
 
724
  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
725
 
 
726
  context = ide_object_get_context (IDE_OBJECT (self));
 
727
  unsaved_files = ide_context_get_unsaved_files (context);
 
728
 
 
729
  task = g_task_new (self, cancellable, callback, user_data);
 
730
 
 
731
  state = g_new0 (CodeCompleteState, 1);
 
732
  state->path = g_file_get_path (file);
 
733
  state->line = gtk_text_iter_get_line (location);
 
734
  state->line_offset = gtk_text_iter_get_line_offset (location);
 
735
  state->unsaved_files = ide_unsaved_files_to_array (unsaved_files);
 
736
 
 
737
  /*
 
738
   * TODO: Technically it is not safe for us to go run this in a thread. We need to ensure
 
739
   *       that only one thread is dealing with this at a time.
 
740
   */
 
741
 
 
742
  g_task_set_task_data (task, state, code_complete_state_free);
 
743
 
 
744
  ide_thread_pool_push_task (IDE_THREAD_POOL_COMPILER,
 
745
                             task,
 
746
                             ide_clang_translation_unit_code_complete_worker);
 
747
 
 
748
  IDE_EXIT;
 
749
}
 
750
 
 
751
/**
 
752
 * ide_clang_translation_unit_code_complete_finish:
 
753
 * @self: A #IdeClangTranslationUnit.
 
754
 * @result: A #GAsyncResult
 
755
 * @error: (out) (nullable): A location for a #GError, or %NULL.
 
756
 *
 
757
 * Completes a call to ide_clang_translation_unit_code_complete_async().
 
758
 *
 
759
 * Returns: (transfer container) (element-type GtkSourceCompletionProposal*): An array of
 
760
 *   #GtkSourceCompletionProposal. Upon failure, %NULL is returned.
 
761
 */
 
762
GPtrArray *
 
763
ide_clang_translation_unit_code_complete_finish (IdeClangTranslationUnit  *self,
 
764
                                                 GAsyncResult             *result,
 
765
                                                 GError                  **error)
 
766
{
 
767
  GTask *task = (GTask *)result;
 
768
  GPtrArray *ret;
 
769
 
 
770
  IDE_ENTRY;
 
771
 
 
772
  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
 
773
  g_return_val_if_fail (G_IS_TASK (task), NULL);
 
774
 
 
775
  ret = g_task_propagate_pointer (task, error);
 
776
 
 
777
  IDE_RETURN (ret);
 
778
}
 
779
 
 
780
static enum CXChildVisitResult
 
781
find_child_type (CXCursor     cursor,
 
782
                 CXCursor     parent,
 
783
                 CXClientData user_data)
 
784
{
 
785
  enum CXCursorKind *child_kind = user_data;
 
786
  enum CXCursorKind kind = clang_getCursorKind (cursor);
 
787
 
 
788
  switch ((int)kind)
 
789
    {
 
790
    case CXCursor_StructDecl:
 
791
    case CXCursor_UnionDecl:
 
792
    case CXCursor_EnumDecl:
 
793
      *child_kind = kind;
 
794
      return CXChildVisit_Break;
 
795
 
 
796
    case CXCursor_TypeRef:
 
797
      cursor = clang_getCursorReferenced (cursor);
 
798
      *child_kind = clang_getCursorKind (cursor);
 
799
      return CXChildVisit_Break;
 
800
 
 
801
    default:
 
802
      break;
 
803
    }
 
804
 
 
805
  return CXChildVisit_Continue;
 
806
}
 
807
 
 
808
static IdeSymbolKind
 
809
get_symbol_kind (CXCursor        cursor,
 
810
                 IdeSymbolFlags *flags)
 
811
{
 
812
  enum CXAvailabilityKind availability;
 
813
  enum CXCursorKind cxkind;
 
814
  IdeSymbolFlags local_flags = 0;
 
815
  IdeSymbolKind kind = 0;
 
816
 
 
817
  availability = clang_getCursorAvailability (cursor);
 
818
  if (availability == CXAvailability_Deprecated)
 
819
    local_flags |= IDE_SYMBOL_FLAGS_IS_DEPRECATED;
 
820
 
 
821
  cxkind = clang_getCursorKind (cursor);
 
822
 
 
823
  if (cxkind == CXCursor_TypedefDecl)
 
824
    {
 
825
      enum CXCursorKind child_kind = 0;
 
826
 
 
827
      clang_visitChildren (cursor, find_child_type, &child_kind);
 
828
      cxkind = child_kind;
 
829
    }
 
830
 
 
831
  switch ((int)cxkind)
 
832
    {
 
833
    case CXCursor_StructDecl:
 
834
      kind = IDE_SYMBOL_STRUCT;
 
835
      break;
 
836
 
 
837
    case CXCursor_UnionDecl:
 
838
      kind = IDE_SYMBOL_UNION;
 
839
      break;
 
840
 
 
841
    case CXCursor_ClassDecl:
 
842
      kind = IDE_SYMBOL_CLASS;
 
843
      break;
 
844
 
 
845
    case CXCursor_FunctionDecl:
 
846
      kind = IDE_SYMBOL_FUNCTION;
 
847
      break;
 
848
 
 
849
    case CXCursor_EnumDecl:
 
850
      kind = IDE_SYMBOL_ENUM;
 
851
      break;
 
852
 
 
853
    case CXCursor_EnumConstantDecl:
 
854
      kind = IDE_SYMBOL_ENUM_VALUE;
 
855
      break;
 
856
 
 
857
    case CXCursor_FieldDecl:
 
858
      kind = IDE_SYMBOL_FIELD;
 
859
      break;
 
860
 
 
861
    default:
 
862
      break;
 
863
    }
 
864
 
 
865
  *flags = local_flags;
 
866
 
 
867
  return kind;
 
868
}
 
869
 
 
870
IdeSymbol *
 
871
ide_clang_translation_unit_lookup_symbol (IdeClangTranslationUnit  *self,
 
872
                                          IdeSourceLocation        *location,
 
873
                                          GError                  **error)
 
874
{
 
875
  g_autofree gchar *filename = NULL;
 
876
  g_autofree gchar *workpath = NULL;
 
877
  g_auto(CXString) cxstr = { 0 };
 
878
  g_autoptr(IdeSourceLocation) declaration = NULL;
 
879
  g_autoptr(IdeSourceLocation) definition = NULL;
 
880
  g_autoptr(IdeSourceLocation) canonical = NULL;
 
881
  IdeSymbolKind symkind = 0;
 
882
  IdeSymbolFlags symflags = 0;
 
883
  IdeProject *project;
 
884
  IdeContext *context;
 
885
  IdeVcs *vcs;
 
886
  GFile *workdir;
 
887
  CXSourceLocation cxlocation;
 
888
  CXCursor tmpcursor;
 
889
  CXCursor cursor;
 
890
  CXFile cxfile;
 
891
  IdeSymbol *ret = NULL;
 
892
  IdeFile *file;
 
893
  GFile *gfile;
 
894
  guint line;
 
895
  guint line_offset;
 
896
 
 
897
  IDE_ENTRY;
 
898
 
 
899
  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
 
900
  g_return_val_if_fail (location != NULL, NULL);
 
901
 
 
902
  context = ide_object_get_context (IDE_OBJECT (self));
 
903
  project = ide_context_get_project (context);
 
904
  vcs = ide_context_get_vcs (context);
 
905
  workdir = ide_vcs_get_working_directory (vcs);
 
906
  workpath = g_file_get_path (workdir);
 
907
 
 
908
  line = ide_source_location_get_line (location);
 
909
  line_offset = ide_source_location_get_line_offset (location);
 
910
 
 
911
  if (!(file = ide_source_location_get_file (location)) ||
 
912
      !(gfile = ide_file_get_file (file)) ||
 
913
      !(filename = g_file_get_path (gfile)) ||
 
914
      !(cxfile = clang_getFile (self->tu, filename)))
 
915
    IDE_RETURN (NULL);
 
916
 
 
917
  cxlocation = clang_getLocation (self->tu, cxfile, line + 1, line_offset + 1);
 
918
  cursor = clang_getCursor (self->tu, cxlocation);
 
919
  if (clang_Cursor_isNull (cursor))
 
920
    IDE_RETURN (NULL);
 
921
 
 
922
  tmpcursor = clang_getCursorReferenced (cursor);
 
923
  if (!clang_Cursor_isNull (tmpcursor))
 
924
    {
 
925
      CXSourceLocation tmploc;
 
926
      CXSourceRange cxrange;
 
927
 
 
928
      cxrange = clang_getCursorExtent (tmpcursor);
 
929
      tmploc = clang_getRangeStart (cxrange);
 
930
      definition = create_location (self, project, workpath, tmploc);
 
931
    }
 
932
 
 
933
  symkind = get_symbol_kind (cursor, &symflags);
 
934
 
 
935
  cxstr = clang_getCursorDisplayName (cursor);
 
936
  ret = _ide_symbol_new (clang_getCString (cxstr), symkind, symflags,
 
937
                         declaration, definition, canonical);
 
938
 
 
939
  /*
 
940
   * TODO: We should also get information about the defintion of the symbol.
 
941
   *       Possibly more.
 
942
   */
 
943
 
 
944
  IDE_RETURN (ret);
 
945
}
 
946
 
 
947
static IdeSymbol *
 
948
create_symbol (CXCursor         cursor,
 
949
               GetSymbolsState *state)
 
950
{
 
951
  g_auto(CXString) cxname = { 0 };
 
952
  g_autoptr(IdeSourceLocation) srcloc = NULL;
 
953
  CXSourceLocation cxloc;
 
954
  IdeSymbolKind symkind;
 
955
  IdeSymbolFlags symflags;
 
956
  const gchar *name;
 
957
  IdeSymbol *symbol;
 
958
  guint line;
 
959
  guint line_offset;
 
960
 
 
961
  cxname = clang_getCursorSpelling (cursor);
 
962
  name = clang_getCString (cxname);
 
963
  cxloc = clang_getCursorLocation (cursor);
 
964
  clang_getFileLocation (cxloc, NULL, &line, &line_offset, NULL);
 
965
  srcloc = ide_source_location_new (state->file, line-1, line_offset-1, 0);
 
966
 
 
967
  symkind = get_symbol_kind (cursor, &symflags);
 
968
 
 
969
  symbol = _ide_symbol_new (name, symkind, symflags, NULL, NULL, srcloc);
 
970
 
 
971
  return symbol;
 
972
}
 
973
 
 
974
static enum CXChildVisitResult
 
975
ide_clang_translation_unit_get_symbols__visitor_cb (CXCursor     cursor,
 
976
                                                    CXCursor     parent,
 
977
                                                    CXClientData user_data)
 
978
{
 
979
  GetSymbolsState *state = user_data;
 
980
  g_autoptr(IdeSymbol) symbol = NULL;
 
981
  g_auto(CXString) filename = { 0 };
 
982
  CXSourceLocation cxloc;
 
983
  CXFile file;
 
984
  enum CXCursorKind kind;
 
985
 
 
986
  g_assert (state);
 
987
 
 
988
  cxloc = clang_getCursorLocation (cursor);
 
989
  clang_getFileLocation (cxloc, &file, NULL, NULL, NULL);
 
990
  filename = clang_getFileName (file);
 
991
 
 
992
  if (0 != g_strcmp0 (clang_getCString (filename), state->path))
 
993
    return CXChildVisit_Continue;
 
994
 
 
995
  kind = clang_getCursorKind (cursor);
 
996
 
 
997
  switch ((int)kind)
 
998
    {
 
999
    case CXCursor_FunctionDecl:
 
1000
    case CXCursor_TypedefDecl:
 
1001
      symbol = create_symbol (cursor, state);
 
1002
      break;
 
1003
 
 
1004
    default:
 
1005
      break;
 
1006
    }
 
1007
 
 
1008
  if (symbol != NULL)
 
1009
    g_ptr_array_add (state->ar, ide_symbol_ref (symbol));
 
1010
 
 
1011
  return CXChildVisit_Continue;
 
1012
}
 
1013
 
 
1014
static gint
 
1015
sort_symbols_by_name (gconstpointer a,
 
1016
                      gconstpointer b)
 
1017
{
 
1018
  IdeSymbol **asym = (IdeSymbol **)a;
 
1019
  IdeSymbol **bsym = (IdeSymbol **)b;
 
1020
 
 
1021
  return g_strcmp0 (ide_symbol_get_name (*asym),
 
1022
                    ide_symbol_get_name (*bsym));
 
1023
}
 
1024
 
 
1025
/**
 
1026
 * ide_clang_translation_unit_get_symbols:
 
1027
 *
 
1028
 * Returns: (transfer container) (element-type IdeSymbol*): An array of #IdeSymbol.
 
1029
 */
 
1030
GPtrArray *
 
1031
ide_clang_translation_unit_get_symbols (IdeClangTranslationUnit *self,
 
1032
                                        IdeFile                 *file)
 
1033
{
 
1034
  GetSymbolsState state = { 0 };
 
1035
  CXCursor cursor;
 
1036
 
 
1037
  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
 
1038
  g_return_val_if_fail (IDE_IS_FILE (file), NULL);
 
1039
 
 
1040
  state.ar = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_symbol_unref);
 
1041
  state.file = file;
 
1042
  state.path = g_file_get_path (ide_file_get_file (file));
 
1043
 
 
1044
  cursor = clang_getTranslationUnitCursor (self->tu);
 
1045
  clang_visitChildren (cursor,
 
1046
                       ide_clang_translation_unit_get_symbols__visitor_cb,
 
1047
                       &state);
 
1048
 
 
1049
  g_ptr_array_sort (state.ar, sort_symbols_by_name);
 
1050
 
 
1051
  g_free (state.path);
 
1052
 
 
1053
  return state.ar;
 
1054
}