ide-clang-translation-unit.c 36.8 KB
Newer Older
1
2
/* ide-clang-translation-unit.c
 *
3
 * Copyright © 2015 Christian Hergert <christian@hergert.me>
4
 *
5
6
7
8
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
9
 *
10
11
12
13
 * 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.
14
15
16
17
18
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

Christian Hergert's avatar
Christian Hergert committed
19
#define G_LOG_DOMAIN "clang-translation-unit"
20

21
#include <clang-c/Index.h>
22
#include <dazzle.h>
23
#include <glib/gi18n.h>
Christian Hergert's avatar
Christian Hergert committed
24
#include <ide.h>
25

26
#include "ide-clang-completion-item.h"
27
#include "ide-clang-completion-item-private.h"
28
#include "ide-clang-private.h"
29
#include "ide-clang-symbol-tree.h"
30
31
#include "ide-clang-translation-unit.h"

32
struct _IdeClangTranslationUnit
33
{
34
35
  IdeObject          parent_instance;

36
  IdeRefPtr         *native;
37
  gint64             serial;
38
  GFile             *file;
39
  IdeHighlightIndex *index;
40
  GHashTable        *diagnostics;
41
};
42

43
44
45
46
47
48
49
50
typedef struct
{
  GPtrArray *unsaved_files;
  gchar     *path;
  guint      line;
  guint      line_offset;
} CodeCompleteState;

51
52
53
54
55
56
57
typedef struct
{
  GPtrArray *ar;
  IdeFile   *file;
  gchar     *path;
} GetSymbolsState;

58
G_DEFINE_TYPE (IdeClangTranslationUnit, ide_clang_translation_unit, IDE_TYPE_OBJECT)
59
DZL_DEFINE_COUNTER (instances, "Clang", "Translation Units", "Number of clang translation units")
60
61
62

enum {
  PROP_0,
63
  PROP_FILE,
64
  PROP_INDEX,
65
66
  PROP_NATIVE,
  PROP_SERIAL,
67
68
69
  LAST_PROP
};

70
static GParamSpec *properties [LAST_PROP];
71

72
73
74
75
76
77
78
79
80
81
82
83
84
static void
code_complete_state_free (gpointer data)
{
  CodeCompleteState *state = data;

  if (state)
    {
      g_clear_pointer (&state->unsaved_files, g_ptr_array_unref);
      g_free (state->path);
      g_free (state);
    }
}

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
static CXFile
get_file_for_location (IdeClangTranslationUnit *self,
                       IdeSourceLocation       *location)
{
  g_autofree gchar *filename = NULL;
  IdeFile *file;
  GFile *gfile;

  g_assert (IDE_IS_CLANG_TRANSLATION_UNIT (self));
  g_assert (location != NULL);

  if (!(file = ide_source_location_get_file (location)) ||
      !(gfile = ide_file_get_file (file)) ||
      !(filename = g_file_get_path (gfile)))
    return NULL;

  return clang_getFile (ide_ref_ptr_get (self->native), filename);
}

104
105
/**
 * ide_clang_translation_unit_get_index:
106
 * @self: an #IdeClangTranslationUnit.
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
 *
 * Gets the highlight index for the translation unit.
 *
 * Returns: (transfer none) (nullable): An #IdeHighlightIndex or %NULL.
 */
IdeHighlightIndex *
ide_clang_translation_unit_get_index (IdeClangTranslationUnit *self)
{
  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);

  return self->index;
}

static void
ide_clang_translation_unit_set_index (IdeClangTranslationUnit *self,
                                      IdeHighlightIndex       *index)
{
  g_assert (IDE_IS_CLANG_TRANSLATION_UNIT (self));

  if (index != NULL)
    self->index = ide_highlight_index_ref (index);
}

130
131
132
133
134
GFile *
ide_clang_translation_unit_get_file (IdeClangTranslationUnit *self)
{
  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);

135
  return self->file;
136
137
138
139
140
141
142
143
144
}

static void
ide_clang_translation_unit_set_file (IdeClangTranslationUnit *self,
                                     GFile                   *file)
{
  g_return_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self));
  g_return_if_fail (G_IS_FILE (file));

145
  if (g_set_object (&self->file, file))
146
    g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_FILE]);
147
148
}

149
150
151
IdeClangTranslationUnit *
_ide_clang_translation_unit_new (IdeContext        *context,
                                 CXTranslationUnit  tu,
152
                                 GFile             *file,
153
                                 IdeHighlightIndex *index,
154
                                 gint64             serial)
155
156
157
158
159
{
  IdeClangTranslationUnit *ret;

  g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
  g_return_val_if_fail (tu, NULL);
160
  g_return_val_if_fail (!file || G_IS_FILE (file), NULL);
161
162
163

  ret = g_object_new (IDE_TYPE_CLANG_TRANSLATION_UNIT,
                      "context", context,
164
165
                      "file", file,
                      "index", index,
166
167
                      "native", tu,
                      "serial", serial,
168
169
170
171
172
                      NULL);

  return ret;
}

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
static IdeDiagnosticSeverity
translate_severity (enum CXDiagnosticSeverity severity)
{
  switch (severity)
    {
    case CXDiagnostic_Ignored:
      return IDE_DIAGNOSTIC_IGNORED;

    case CXDiagnostic_Note:
      return IDE_DIAGNOSTIC_NOTE;

    case CXDiagnostic_Warning:
      return IDE_DIAGNOSTIC_WARNING;

    case CXDiagnostic_Error:
      return IDE_DIAGNOSTIC_ERROR;

    case CXDiagnostic_Fatal:
      return IDE_DIAGNOSTIC_FATAL;

    default:
      return 0;
    }
}

198
199
200
201
static gchar *
get_path (const gchar *workpath,
          const gchar *path)
{
202
203
204
205
206
207
  if (path == NULL)
    return g_strdup (workpath);
  else if (g_str_has_prefix (path, workpath))
    return g_strdup (path);
  else
    return g_build_filename (workpath, path, NULL);
208
209
}

210
211
static IdeSourceLocation *
create_location (IdeClangTranslationUnit *self,
212
                 const gchar             *workpath,
213
214
                 CXSourceLocation         cxloc)
{
215
  g_autofree gchar *path = NULL;
216
217
218
219
220
  g_autoptr(IdeFile) file = NULL;
  g_autoptr(GFile) gfile = NULL;
  g_auto(CXString) str = {0};
  IdeContext *context;
  CXFile cxfile = NULL;
221
222
223
224
  unsigned line;
  unsigned column;
  unsigned offset;

225
226
  g_return_val_if_fail (self != NULL, NULL);
  g_return_val_if_fail (workpath != NULL, NULL);
227
228
229

  clang_getFileLocation (cxloc, &cxfile, &line, &column, &offset);

230
231
232
  if (line > 0) line--;
  if (column > 0) column--;

233
  str = clang_getFileName (cxfile);
234
  path = get_path (workpath, clang_getCString (str));
235

236
237
238
  context = ide_object_get_context (IDE_OBJECT (self));
  gfile = g_file_new_for_path (path);
  file = ide_file_new (context, gfile);
239

240
  return ide_source_location_new (file, line, column, offset);
241
242
243
244
}

static IdeSourceRange *
create_range (IdeClangTranslationUnit *self,
245
              const gchar             *workpath,
246
247
              CXSourceRange            cxrange)
{
248
  IdeSourceRange *range = NULL;
249
250
251
252
253
254
255
256
257
258
  CXSourceLocation cxbegin;
  CXSourceLocation cxend;
  g_autoptr(IdeSourceLocation) begin = NULL;
  g_autoptr(IdeSourceLocation) end = NULL;

  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);

  cxbegin = clang_getRangeStart (cxrange);
  cxend = clang_getRangeEnd (cxrange);

259
260
  begin = create_location (self, workpath, cxbegin);
  end = create_location (self, workpath, cxend);
261

262
  if ((begin != NULL) && (end != NULL))
263
    range = ide_source_range_new (begin, end);
264
265
266
267

  return range;
}

268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
static gboolean
cxfile_equal (CXFile  cxfile,
              GFile  *file)
{
  CXString cxstr;
  gchar *path;
  gboolean ret;

  cxstr = clang_getFileName (cxfile);
  path = g_file_get_path (file);

  ret = (0 == g_strcmp0 (clang_getCString (cxstr), path));

  clang_disposeString (cxstr);
  g_free (path);

  return ret;
}

287
288
static IdeDiagnostic *
create_diagnostic (IdeClangTranslationUnit *self,
289
                   const gchar             *workpath,
290
                   GFile                   *target,
291
292
                   CXDiagnostic            *cxdiag)
{
293
  g_autoptr(IdeSourceLocation) loc = NULL;
294
295
  enum CXDiagnosticSeverity cxseverity;
  IdeDiagnosticSeverity severity;
296
  IdeDiagnostic *diag;
297
  g_autofree gchar *spelling = NULL;
298
  CXString cxstr;
299
  CXSourceLocation cxloc;
300
  CXFile cxfile = NULL;
301
302
  guint num_ranges;
  guint i;
303
304
305
306

  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
  g_return_val_if_fail (cxdiag, NULL);

307
308
309
  cxloc = clang_getDiagnosticLocation (cxdiag);
  clang_getExpansionLocation (cxloc, &cxfile, NULL, NULL, NULL);

310
  if (cxfile && !cxfile_equal (cxfile, target))
311
312
    return NULL;

313
314
315
  cxseverity = clang_getDiagnosticSeverity (cxdiag);
  severity = translate_severity (cxseverity);

316
317
318
319
  cxstr = clang_getDiagnosticSpelling (cxdiag);
  spelling = g_strdup (clang_getCString (cxstr));
  clang_disposeString (cxstr);

320
321
322
323
324
325
326
327
328
329
330
331
  /*
   * I thought we could use an approach like the following to get deprecation
   * status. However, it has so far proven ineffective.
   *
   *   cursor = clang_getCursor (self->tu, cxloc);
   *   avail = clang_getCursorAvailability (cursor);
   */
  if ((severity == IDE_DIAGNOSTIC_WARNING) &&
      (spelling != NULL) &&
      (strstr (spelling, "deprecated") != NULL))
    severity = IDE_DIAGNOSTIC_DEPRECATED;

332
  loc = create_location (self, workpath, cxloc);
333

334
  diag = ide_diagnostic_new (severity, spelling, loc);
335

336
337
  num_ranges = clang_getDiagnosticNumRanges (cxdiag);

338
339
340
341
342
343
  for (i = 0; i < num_ranges; i++)
    {
      CXSourceRange cxrange;
      IdeSourceRange *range;

      cxrange = clang_getDiagnosticRange (cxdiag, i);
344
      range = create_range (self, workpath, cxrange);
345
      if (range != NULL)
346
        ide_diagnostic_take_range (diag, range);
347
348
349
    }

  return diag;
350
351
}

352
/**
353
 * ide_clang_translation_unit_get_diagnostics_for_file:
354
 *
355
 * Retrieves the diagnostics for the translation unit for a specific file.
356
357
358
359
 *
 * Returns: (transfer none) (nullable): An #IdeDiagnostics or %NULL.
 */
IdeDiagnostics *
360
361
ide_clang_translation_unit_get_diagnostics_for_file (IdeClangTranslationUnit *self,
                                                     GFile                   *file)
362
363
364
{
  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);

365
  if (!g_hash_table_contains (self->diagnostics, file))
366
    {
367
      CXTranslationUnit tu = ide_ref_ptr_get (self->native);
368
369
      g_autofree gchar *workpath = NULL;
      g_autoptr(GPtrArray) diags = NULL;
370
      IdeContext *context;
371
      GFile *workdir;
372
      IdeVcs *vcs;
373
374
      guint count;

375
      diags = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_diagnostic_unref);
376

377
      context = ide_object_get_context (IDE_OBJECT (self));
378
379
380
      vcs = ide_context_get_vcs (context);
      workdir = ide_vcs_get_working_directory (vcs);
      workpath = g_file_get_path (workdir);
381

382
      count = clang_getNumDiagnostics (tu);
383
      for (guint i = 0; i < count; i++)
384
385
        {
          CXDiagnostic cxdiag;
386
          IdeDiagnostic *diag;
387

388
          cxdiag = clang_getDiagnostic (tu, i);
389
          diag = create_diagnostic (self, workpath, file, cxdiag);
390
391
392
393
394
395
396

          if (diag != NULL)
            {
              guint num_fixits;

              num_fixits = clang_getDiagnosticNumFixIts (cxdiag);

397
              for (guint j = 0; j < num_fixits; j++)
398
                {
399
                  g_autoptr(IdeSourceRange) range = NULL;
400
                  g_autoptr(IdeFixit) fixit = NULL;
401
402
403
404
                  CXSourceRange cxrange;
                  CXString cxstr;

                  cxstr = clang_getDiagnosticFixIt (cxdiag, j, &cxrange);
405
                  range = create_range (self, workpath, cxrange);
406
                  fixit = ide_fixit_new (range, clang_getCString (cxstr));
407
408
409
                  clang_disposeString (cxstr);

                  if (fixit != NULL)
410
                    ide_diagnostic_take_fixit (diag, g_steal_pointer (&fixit));
411
412
413
414
415
                }

              g_ptr_array_add (diags, diag);
            }

416
417
418
          clang_disposeDiagnostic (cxdiag);
        }

419
420
421
      g_hash_table_insert (self->diagnostics,
                           g_object_ref (file),
                           ide_diagnostics_new (g_steal_pointer (&diags)));
422
423
    }

424
425
426
427
428
429
430
431
432
433
434
435
436
437
  return g_hash_table_lookup (self->diagnostics, file);
}

/**
 * ide_clang_translation_unit_get_diagnostics:
 *
 * Retrieves the diagnostics for the translation unit.
 *
 * Returns: (transfer none) (nullable): An #IdeDiagnostics or %NULL.
 */
IdeDiagnostics *
ide_clang_translation_unit_get_diagnostics (IdeClangTranslationUnit *self)
{
  return ide_clang_translation_unit_get_diagnostics_for_file (self, self->file);
438
439
}

440
gint64
441
ide_clang_translation_unit_get_serial (IdeClangTranslationUnit *self)
442
443
444
{
  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), -1);

445
  return self->serial;
446
447
}

448
449
450
451
452
453
454
455
456
457
static void
ide_clang_translation_unit_set_native (IdeClangTranslationUnit *self,
                                       CXTranslationUnit        native)
{
  g_assert (IDE_IS_CLANG_TRANSLATION_UNIT (self));

  if (native != NULL)
    self->native = ide_ref_ptr_new (native, (GDestroyNotify)clang_disposeTranslationUnit);
}

458
459
460
461
462
static void
ide_clang_translation_unit_finalize (GObject *object)
{
  IdeClangTranslationUnit *self = (IdeClangTranslationUnit *)object;

463
464
  IDE_ENTRY;

465
  g_clear_pointer (&self->native, ide_ref_ptr_unref);
466
  g_clear_object (&self->file);
467
  g_clear_pointer (&self->index, ide_highlight_index_unref);
468
  g_clear_pointer (&self->diagnostics, g_hash_table_unref);
469
470

  G_OBJECT_CLASS (ide_clang_translation_unit_parent_class)->finalize (object);
471

472
  DZL_COUNTER_DEC (instances);
473

474
  IDE_EXIT;
475
476
477
478
479
480
481
482
483
484
485
486
}

static void
ide_clang_translation_unit_get_property (GObject    *object,
                                         guint       prop_id,
                                         GValue     *value,
                                         GParamSpec *pspec)
{
  IdeClangTranslationUnit *self = IDE_CLANG_TRANSLATION_UNIT (object);

  switch (prop_id)
    {
487
488
489
490
    case PROP_FILE:
      g_value_set_object (value, ide_clang_translation_unit_get_file (self));
      break;

491
492
493
494
    case PROP_INDEX:
      g_value_set_boxed (value, ide_clang_translation_unit_get_index (self));
      break;

495
496
    case PROP_SERIAL:
      g_value_set_int64 (value, ide_clang_translation_unit_get_serial (self));
497
498
499
500
501
502
503
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

504
505
506
507
508
509
510
511
512
513
514
515
516
517
static void
ide_clang_translation_unit_set_property (GObject      *object,
                                         guint         prop_id,
                                         const GValue *value,
                                         GParamSpec   *pspec)
{
  IdeClangTranslationUnit *self = IDE_CLANG_TRANSLATION_UNIT (object);

  switch (prop_id)
    {
    case PROP_FILE:
      ide_clang_translation_unit_set_file (self, g_value_get_object (value));
      break;

518
519
520
521
    case PROP_INDEX:
      ide_clang_translation_unit_set_index (self, g_value_get_boxed (value));
      break;

522
523
524
525
526
    case PROP_SERIAL:
      self->serial = g_value_get_int64 (value);
      break;

    case PROP_NATIVE:
527
      ide_clang_translation_unit_set_native (self, g_value_get_pointer (value));
528
529
      break;

530
531
532
533
534
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

535
536
537
538
539
540
541
static void
ide_clang_translation_unit_class_init (IdeClangTranslationUnitClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->finalize = ide_clang_translation_unit_finalize;
  object_class->get_property = ide_clang_translation_unit_get_property;
542
543
  object_class->set_property = ide_clang_translation_unit_set_property;

544
  properties [PROP_FILE] =
545
    g_param_spec_object ("file",
Piotr Drąg's avatar
Piotr Drąg committed
546
547
                         "File",
                         "The file used to build the translation unit.",
548
549
                         G_TYPE_FILE,
                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
550

551
  properties [PROP_INDEX] =
552
    g_param_spec_boxed ("index",
Piotr Drąg's avatar
Piotr Drąg committed
553
554
                         "Index",
                         "The highlight index for the translation unit.",
555
                         IDE_TYPE_HIGHLIGHT_INDEX,
556
                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
557

558
  properties [PROP_NATIVE] =
559
    g_param_spec_pointer ("native",
Piotr Drąg's avatar
Piotr Drąg committed
560
561
                          "Native",
                          "The native translation unit pointer.",
562
563
                          (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));

564
  properties [PROP_SERIAL] =
565
    g_param_spec_int64 ("serial",
Piotr Drąg's avatar
Piotr Drąg committed
566
567
                        "Serial",
                        "A sequence number for the translation unit.",
568
                        0,
569
570
                        G_MAXINT64,
                        0,
571
                        (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
572

573
  g_object_class_install_properties (object_class, LAST_PROP, properties);
574
575
576
577
578
}

static void
ide_clang_translation_unit_init (IdeClangTranslationUnit *self)
{
579
  DZL_COUNTER_INC (instances);
580

581
582
583
584
  self->diagnostics = g_hash_table_new_full ((GHashFunc)g_file_hash,
                                             (GEqualFunc)g_file_equal,
                                             g_object_unref,
                                             (GDestroyNotify)ide_diagnostics_unref);
585
}
586
587
588
589
590
591
592
593
594
595

static void
ide_clang_translation_unit_code_complete_worker (GTask        *task,
                                                 gpointer      source_object,
                                                 gpointer      task_data,
                                                 GCancellable *cancellable)
{
  IdeClangTranslationUnit *self = source_object;
  CodeCompleteState *state = task_data;
  CXCodeCompleteResults *results;
596
  CXTranslationUnit tu;
597
598
  g_autoptr(IdeRefPtr) refptr = NULL;
  struct CXUnsavedFile *ufs;
599
  GPtrArray *ar;
600
601
602
603
604
605
606
  gsize i;
  gsize j = 0;

  g_assert (IDE_IS_CLANG_TRANSLATION_UNIT (self));
  g_assert (state);
  g_assert (state->unsaved_files);

607
608
  tu = ide_ref_ptr_get (self->native);

609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
  /*
   * FIXME: Not thread safe! We should probably add a "Pending" flag or something that is
   *        similar to g_input_stream_set_pending().
   */

  if (!state->path)
    {
      /* implausable to reach here, anyway */
      g_task_return_new_error (task,
                               G_IO_ERROR,
                               G_IO_ERROR_INVALID_FILENAME,
                               _("clang_codeCompleteAt() only works on local files"));
      return;
    }

  ufs = g_new0 (struct CXUnsavedFile, state->unsaved_files->len);

  for (i = 0; i < state->unsaved_files->len; i++)
    {
      IdeUnsavedFile *uf;
      gchar *path;
      GFile *file;

      uf = g_ptr_array_index (state->unsaved_files, i);
      file = ide_unsaved_file_get_file (uf);
      path = g_file_get_path (file);

      /*
       * NOTE: Some files might not be local, and therefore return a NULL path.
       *       Also, we will free the path from the (const char *) pointer after
       *       executing the work.
       */
      if (path != NULL)
        {
          GBytes *content = ide_unsaved_file_get_content (uf);

          ufs [j].Filename = path;
          ufs [j].Contents = g_bytes_get_data (content, NULL);
          ufs [j].Length = g_bytes_get_size (content);

          j++;
        }
    }

653
  results = clang_codeCompleteAt (tu,
654
655
656
657
658
659
660
661
662
663
664
                                  state->path,
                                  state->line + 1,
                                  state->line_offset + 1,
                                  ufs, j,
                                  clang_defaultCodeCompleteOptions ());

  /*
   * encapsulate in refptr so we don't need to malloc lots of little strings.
   * we will inflate result strings as necessary.
   */
  refptr = ide_ref_ptr_new (results, (GDestroyNotify)clang_disposeCodeCompleteResults);
665
  ar = g_ptr_array_new_with_free_func (g_object_unref);
666
667

  for (i = 0; i < results->NumResults; i++)
668
    g_ptr_array_add (ar, ide_clang_completion_item_new (refptr, i));
669

670
  g_task_return_pointer (task, ar, (GDestroyNotify)g_ptr_array_unref);
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691

  /* cleanup malloc'd state */
  for (i = 0; i < j; i++)
    g_free ((gchar *)ufs [i].Filename);
  g_free (ufs);
}


void
ide_clang_translation_unit_code_complete_async (IdeClangTranslationUnit *self,
                                                GFile                   *file,
                                                const GtkTextIter       *location,
                                                GCancellable            *cancellable,
                                                GAsyncReadyCallback      callback,
                                                gpointer                 user_data)
{
  g_autoptr(GTask) task = NULL;
  CodeCompleteState *state;
  IdeContext *context;
  IdeUnsavedFiles *unsaved_files;

692
693
  IDE_ENTRY;

694
695
696
697
698
699
700
701
702
703
704
705
706
707
  g_return_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self));
  g_return_if_fail (G_IS_FILE (file));
  g_return_if_fail (location);
  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));

  context = ide_object_get_context (IDE_OBJECT (self));
  unsaved_files = ide_context_get_unsaved_files (context);

  task = g_task_new (self, cancellable, callback, user_data);

  state = g_new0 (CodeCompleteState, 1);
  state->path = g_file_get_path (file);
  state->line = gtk_text_iter_get_line (location);
  state->line_offset = gtk_text_iter_get_line_offset (location);
708
  state->unsaved_files = ide_unsaved_files_to_array (unsaved_files);
709
710
711
712
713
714
715

  /*
   * TODO: Technically it is not safe for us to go run this in a thread. We need to ensure
   *       that only one thread is dealing with this at a time.
   */

  g_task_set_task_data (task, state, code_complete_state_free);
716
717
718
719

  ide_thread_pool_push_task (IDE_THREAD_POOL_COMPILER,
                             task,
                             ide_clang_translation_unit_code_complete_worker);
720
721

  IDE_EXIT;
722
723
724
725
}

/**
 * ide_clang_translation_unit_code_complete_finish:
726
 * @self: an #IdeClangTranslationUnit.
727
 * @result: a #GAsyncResult
728
729
730
731
 * @error: (out) (nullable): A location for a #GError, or %NULL.
 *
 * Completes a call to ide_clang_translation_unit_code_complete_async().
 *
732
733
 * Returns: (transfer container) (element-type GtkSourceCompletionProposal*): An array of
 *   #GtkSourceCompletionProposal. Upon failure, %NULL is returned.
734
 */
735
GPtrArray *
736
737
738
739
740
ide_clang_translation_unit_code_complete_finish (IdeClangTranslationUnit  *self,
                                                 GAsyncResult             *result,
                                                 GError                  **error)
{
  GTask *task = (GTask *)result;
741
742
743
  GPtrArray *ret;

  IDE_ENTRY;
744
745
746
747

  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
  g_return_val_if_fail (G_IS_TASK (task), NULL);

748
749
750
  ret = g_task_propagate_pointer (task, error);

  IDE_RETURN (ret);
751
}
752

753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
static enum CXChildVisitResult
find_child_type (CXCursor     cursor,
                 CXCursor     parent,
                 CXClientData user_data)
{
  enum CXCursorKind *child_kind = user_data;
  enum CXCursorKind kind = clang_getCursorKind (cursor);

  switch ((int)kind)
    {
    case CXCursor_StructDecl:
    case CXCursor_UnionDecl:
    case CXCursor_EnumDecl:
      *child_kind = kind;
      return CXChildVisit_Break;

    case CXCursor_TypeRef:
      cursor = clang_getCursorReferenced (cursor);
      *child_kind = clang_getCursorKind (cursor);
      return CXChildVisit_Break;

    default:
      break;
    }

  return CXChildVisit_Continue;
}

781
782
783
784
785
static IdeSymbolKind
get_symbol_kind (CXCursor        cursor,
                 IdeSymbolFlags *flags)
{
  enum CXAvailabilityKind availability;
786
  enum CXCursorKind cxkind;
787
788
789
790
791
792
793
  IdeSymbolFlags local_flags = 0;
  IdeSymbolKind kind = 0;

  availability = clang_getCursorAvailability (cursor);
  if (availability == CXAvailability_Deprecated)
    local_flags |= IDE_SYMBOL_FLAGS_IS_DEPRECATED;

794
795
796
797
798
799
800
801
802
803
804
  cxkind = clang_getCursorKind (cursor);

  if (cxkind == CXCursor_TypedefDecl)
    {
      enum CXCursorKind child_kind = 0;

      clang_visitChildren (cursor, find_child_type, &child_kind);
      cxkind = child_kind;
    }

  switch ((int)cxkind)
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
    {
    case CXCursor_StructDecl:
      kind = IDE_SYMBOL_STRUCT;
      break;

    case CXCursor_UnionDecl:
      kind = IDE_SYMBOL_UNION;
      break;

    case CXCursor_ClassDecl:
      kind = IDE_SYMBOL_CLASS;
      break;

    case CXCursor_FunctionDecl:
      kind = IDE_SYMBOL_FUNCTION;
      break;

    case CXCursor_EnumDecl:
      kind = IDE_SYMBOL_ENUM;
      break;

    case CXCursor_EnumConstantDecl:
      kind = IDE_SYMBOL_ENUM_VALUE;
      break;

    case CXCursor_FieldDecl:
      kind = IDE_SYMBOL_FIELD;
      break;

Debarshi's avatar
Debarshi committed
834
835
836
837
    case CXCursor_InclusionDirective:
      kind = IDE_SYMBOL_HEADER;
      break;

838
839
840
841
842
843
844
845
846
    default:
      break;
    }

  *flags = local_flags;

  return kind;
}

847
848
849
850
851
IdeSymbol *
ide_clang_translation_unit_lookup_symbol (IdeClangTranslationUnit  *self,
                                          IdeSourceLocation        *location,
                                          GError                  **error)
{
852
  g_autofree gchar *workpath = NULL;
853
  g_auto(CXString) cxstr = { 0 };
854
855
856
  g_autoptr(IdeSourceLocation) declaration = NULL;
  g_autoptr(IdeSourceLocation) definition = NULL;
  g_autoptr(IdeSourceLocation) canonical = NULL;
857
  CXTranslationUnit tu;
858
859
  IdeSymbolKind symkind = 0;
  IdeSymbolFlags symflags = 0;
860
861
862
  IdeContext *context;
  IdeVcs *vcs;
  GFile *workdir;
863
  CXSourceLocation cxlocation;
864
  CXCursor tmpcursor;
865
866
867
868
869
870
871
872
873
874
875
  CXCursor cursor;
  CXFile cxfile;
  IdeSymbol *ret = NULL;
  guint line;
  guint line_offset;

  IDE_ENTRY;

  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
  g_return_val_if_fail (location != NULL, NULL);

876
877
  tu = ide_ref_ptr_get (self->native);

878
879
880
881
882
  context = ide_object_get_context (IDE_OBJECT (self));
  vcs = ide_context_get_vcs (context);
  workdir = ide_vcs_get_working_directory (vcs);
  workpath = g_file_get_path (workdir);

883
884
885
  line = ide_source_location_get_line (location);
  line_offset = ide_source_location_get_line_offset (location);

886
  if (NULL == (cxfile = get_file_for_location (self, location)))
887
888
    IDE_RETURN (NULL);

889
890
  cxlocation = clang_getLocation (tu, cxfile, line + 1, line_offset + 1);
  cursor = clang_getCursor (tu, cxlocation);
891
892
893
  if (clang_Cursor_isNull (cursor))
    IDE_RETURN (NULL);

Anoop Chandu's avatar
Anoop Chandu committed
894
895
896
897
898
  tmpcursor = clang_getCursorDefinition (cursor);

  if (clang_Cursor_isNull (tmpcursor))
    tmpcursor = clang_getCursorReferenced (cursor);

899
900
901
902
903
904
905
  if (!clang_Cursor_isNull (tmpcursor))
    {
      CXSourceLocation tmploc;
      CXSourceRange cxrange;

      cxrange = clang_getCursorExtent (tmpcursor);
      tmploc = clang_getRangeStart (cxrange);
Anoop Chandu's avatar
Anoop Chandu committed
906
907

      if (clang_isCursorDefinition (tmpcursor))
908
        definition = create_location (self, workpath, tmploc);
Anoop Chandu's avatar
Anoop Chandu committed
909
      else
910
        declaration = create_location (self, workpath, tmploc);
911
912
    }

913
914
  symkind = get_symbol_kind (cursor, &symflags);

Debarshi's avatar
Debarshi committed
915
916
  if (symkind == IDE_SYMBOL_HEADER)
    {
Christian Hergert's avatar
Christian Hergert committed
917
      g_auto(CXString) included_file_name = {0};
Debarshi's avatar
Debarshi committed
918
      CXFile included_file;
919
      const gchar *path;
Debarshi's avatar
Debarshi committed
920
921
922

      included_file = clang_getIncludedFile (cursor);
      included_file_name = clang_getFileName (included_file);
923
      path = clang_getCString (included_file_name);
Debarshi's avatar
Debarshi committed
924

925
926
      if (path != NULL)
        {
Christian Hergert's avatar
Christian Hergert committed
927
928
929
          g_autoptr(IdeFile) file = NULL;
          g_autoptr(GFile) gfile = NULL;

930
          gfile = g_file_new_for_path (path);
Christian Hergert's avatar
Christian Hergert committed
931
          file = ide_file_new (context, gfile);
932
933

          g_clear_pointer (&definition, ide_symbol_unref);
Anoop Chandu's avatar
Anoop Chandu committed
934
          declaration = ide_source_location_new (file, 0, 0, 0);
935
        }
Debarshi's avatar
Debarshi committed
936
937
    }

938
  cxstr = clang_getCursorDisplayName (cursor);
939
940
  ret = ide_symbol_new (clang_getCString (cxstr), symkind, symflags,
                        declaration, definition, canonical);
941
942
943
944
945
946
947
948

  /*
   * TODO: We should also get information about the defintion of the symbol.
   *       Possibly more.
   */

  IDE_RETURN (ret);
}
949
950
951
952
953
954
955
956

static IdeSymbol *
create_symbol (CXCursor         cursor,
               GetSymbolsState *state)
{
  g_auto(CXString) cxname = { 0 };
  g_autoptr(IdeSourceLocation) srcloc = NULL;
  CXSourceLocation cxloc;
957
958
  IdeSymbolKind symkind;
  IdeSymbolFlags symflags;
959
960
961
962
963
964
965
966
967
968
969
  const gchar *name;
  IdeSymbol *symbol;
  guint line;
  guint line_offset;

  cxname = clang_getCursorSpelling (cursor);
  name = clang_getCString (cxname);
  cxloc = clang_getCursorLocation (cursor);
  clang_getFileLocation (cxloc, NULL, &line, &line_offset, NULL);
  srcloc = ide_source_location_new (state->file, line-1, line_offset-1, 0);

970
971
  symkind = get_symbol_kind (cursor, &symflags);

972
  symbol = ide_symbol_new (name, symkind, symflags, NULL, NULL, srcloc);
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999

  return symbol;
}

static enum CXChildVisitResult
ide_clang_translation_unit_get_symbols__visitor_cb (CXCursor     cursor,
                                                    CXCursor     parent,
                                                    CXClientData user_data)
{
  GetSymbolsState *state = user_data;
  g_autoptr(IdeSymbol) symbol = NULL;
  g_auto(CXString) filename = { 0 };
  CXSourceLocation cxloc;
  CXFile file;
  enum CXCursorKind kind;

  g_assert (state);

  cxloc = clang_getCursorLocation (cursor);
  clang_getFileLocation (cxloc, &file, NULL, NULL, NULL);
  filename = clang_getFileName (file);

  if (0 != g_strcmp0 (clang_getCString (filename), state->path))
    return CXChildVisit_Continue;

  kind = clang_getCursorKind (cursor);

1000
  switch ((int)kind)
1001
1002
    {
    case CXCursor_FunctionDecl:
1003
    case CXCursor_TypedefDecl:
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
      symbol = create_symbol (cursor, state);
      break;

    default:
      break;
    }

  if (symbol != NULL)
    g_ptr_array_add (state->ar, ide_symbol_ref (symbol));

  return CXChildVisit_Continue;
}

static gint
sort_symbols_by_name (gconstpointer a,
                      gconstpointer b)
{
  IdeSymbol **asym = (IdeSymbol **)a;
  IdeSymbol **bsym = (IdeSymbol **)b;

  return g_strcmp0 (ide_symbol_get_name (*asym),
                    ide_symbol_get_name (*bsym));
}

/**
 * ide_clang_translation_unit_get_symbols:
 *
Christian Hergert's avatar
Christian Hergert committed
1031
 * Returns: (transfer container) (element-type Ide.Symbol): An array of #IdeSymbol.
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
 */
GPtrArray *
ide_clang_translation_unit_get_symbols (IdeClangTranslationUnit *self,
                                        IdeFile                 *file)
{
  GetSymbolsState state = { 0 };
  CXCursor cursor;

  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
  g_return_val_if_fail (IDE_IS_FILE (file), NULL);

  state.ar = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_symbol_unref);
  state.file = file;
  state.path = g_file_get_path (ide_file_get_file (file));

1047
  cursor = clang_getTranslationUnitCursor (ide_ref_ptr_get (self->native));
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
  clang_visitChildren (cursor,
                       ide_clang_translation_unit_get_symbols__visitor_cb,
                       &state);

  g_ptr_array_sort (state.ar, sort_symbols_by_name);

  g_free (state.path);

  return state.ar;
}
1058
1059
1060
1061
1062
1063
1064
1065
1066

void
ide_clang_translation_unit_get_symbol_tree_async (IdeClangTranslationUnit *self,
                                                  GFile                   *file,
                                                  GCancellable            *cancellable,
                                                  GAsyncReadyCallback      callback,
                                                  gpointer                 user_data)
{
  g_autoptr(GTask) task = NULL;
Christian Hergert's avatar
Christian Hergert committed
1067
  g_autoptr(IdeSymbolTree) symbol_tree = NULL;
1068
  IdeContext *context;
1069
1070
1071
1072
1073
1074

  g_return_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self));
  g_return_if_fail (G_IS_FILE (file));
  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));

  task = g_task_new (self, cancellable, callback, user_data);
Christian Hergert's avatar
Christian Hergert committed
1075
1076
  g_task_set_source_tag (task, ide_clang_translation_unit_get_symbol_tree_async);
  g_task_set_priority (task, G_PRIORITY_LOW);
1077
1078

  context = ide_object_get_context (IDE_OBJECT (self));
1079
  symbol_tree = g_object_new (IDE_TYPE_CLANG_SYMBOL_TREE,
1080
                              "context", context,
1081
1082
1083
                              "native", self->native,
                              "file", file,
                              NULL);
Christian Hergert's avatar
Christian Hergert committed
1084
  g_task_return_pointer (task, g_steal_pointer (&symbol_tree), g_object_unref);
1085
1086
1087
1088
1089
1090
1091
1092
}

IdeSymbolTree *
ide_clang_translation_unit_get_symbol_tree_finish (IdeClangTranslationUnit  *self,
                                                   GAsyncResult             *result,
                                                   GError                  **error)
{
  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
Christian Hergert's avatar
Christian Hergert committed
1093
  g_return_val_if_fail (G_IS_TASK (result), NULL);
1094

Christian Hergert's avatar
Christian Hergert committed
1095
  return g_task_propagate_pointer (G_TASK (result), error);
1096
}
1097

1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
static gboolean
is_ignored_kind (enum CXCursorKind kind)
{
  switch ((int)kind)
    {
    case CXCursor_CXXMethod:
    case CXCursor_ClassDecl:
    case CXCursor_ClassTemplate:
    case CXCursor_Constructor:
    case CXCursor_Destructor:
    case CXCursor_EnumConstantDecl:
    case CXCursor_EnumDecl:
    case CXCursor_FunctionDecl:
    case CXCursor_FunctionTemplate:
    case CXCursor_Namespace:
    case CXCursor_NamespaceAlias:
    case CXCursor_StructDecl:
    case CXCursor_TranslationUnit:
    case CXCursor_TypeAliasDecl:
    case CXCursor_TypedefDecl:
    case CXCursor_UnionDecl:
      return FALSE;

    default:
      return TRUE;
    }
}

static CXCursor
move_to_previous_sibling (CXTranslationUnit unit,
                          CXCursor          cursor)
{
  CXSourceRange range = clang_getCursorExtent (cursor);
  CXSourceLocation begin = clang_getRangeStart (range);
  CXSourceLocation loc;
  CXFile file;
  unsigned line;
  unsigned column;

  clang_getFileLocation (begin, &file, &line, &column, NULL);
  loc = clang_getLocation (unit, file, line, column - 1);

  return clang_getCursor (unit, loc);
}

1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
/**
 * ide_clang_translation_unit_find_nearest_scope:
 * @self: a #IdeClangTranslationUnit
 * @location: An #IdeSourceLocation within the unit
 * @error: A location for a #GError or %NULL
 *
 * This locates the nearest scope for @location and returns it
 * as an #IdeSymbol.
 *
 * Returns: (transfer full): An #IdeSymbol or %NULL and @error is set.
 *
 * Since: 3.26
 */
IdeSymbol *
ide_clang_translation_unit_find_nearest_scope (IdeClangTranslationUnit  *self,
                                               IdeSourceLocation        *location,
                                               GError                  **error)
{
  g_autoptr(IdeSourceLocation) symbol_location = NULL;
  g_auto(CXString) cxname = { 0 };
  CXTranslationUnit unit;
  CXSourceLocation loc;
  CXCursor cursor;
1166
  enum CXCursorKind kind;
1167
1168
  IdeSymbolKind symkind;
  IdeSymbolFlags symflags;
1169
  IdeSymbol *ret = NULL;
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
  CXFile file;
  IdeFile *ifile;
  guint line;
  guint line_offset;

  IDE_ENTRY;

  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);
  g_return_val_if_fail (location != NULL, NULL);

  ifile = ide_source_location_get_file (location);
  line = ide_source_location_get_line (location);
  line_offset = ide_source_location_get_line_offset (location);

  if (NULL == (file = get_file_for_location (self, location)))
    {
      g_set_error (error,
                   G_IO_ERROR,
                   G_IO_ERROR_FAILED,
                   "Failed to locate file in translation unit");
      IDE_RETURN (ret);
    }

  unit = ide_ref_ptr_get (self->native);
  loc = clang_getLocation (unit, file, line + 1, line_offset + 1);
  cursor = clang_getCursor (unit, loc);

  if (clang_Cursor_isNull (cursor))
    {
      g_set_error (error,
                   G_IO_ERROR,
                   G_IO_ERROR_FAILED,
                   "Location was not found in translation unit");
      IDE_RETURN (ret);
    }

1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
  /*
   * Macros sort of mess us up and result in us thinking
   * we are in some sort of InvalidFile condition.
   */
  kind = clang_getCursorKind (cursor);
  if (kind == CXCursor_MacroExpansion)
    cursor = move_to_previous_sibling (unit, cursor);

  /*
   * The semantic parent may still be uninteresting to us,
   * so possibly keep walking up the AST until we get to
   * something better.
   */
  do
    {
      cursor = clang_getCursorSemanticParent (cursor);
      kind = clang_getCursorKind (cursor);
    }
  while (!clang_Cursor_isNull (cursor) && is_ignored_kind (kind));

  if (kind == CXCursor_TranslationUnit)
    {
      g_set_error (error,
                   G_IO_ERROR,
                   G_IO_ERROR_NOT_FOUND,
                   "The location does not have a semantic parent");
      IDE_RETURN (NULL);
    }
1234
1235

  symbol_location = ide_source_location_new (ifile, line - 1, line_offset - 1, 0);
1236
  cxname = clang_getCursorSpelling (cursor);
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
  symkind = get_symbol_kind (cursor, &symflags);

  ret = ide_symbol_new (clang_getCString (cxname),
                        symkind,
                        symflags,
                        NULL,
                        NULL,
                        symbol_location);

  IDE_TRACE_MSG ("Symbol = %p", ret);

  IDE_RETURN (ret);
}
Anoop Chandu's avatar
Anoop Chandu committed
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296

gchar *
ide_clang_translation_unit_generate_key (IdeClangTranslationUnit  *self,
                                         IdeSourceLocation        *location)
{
  CXTranslationUnit unit;
  CXFile file;
  CXSourceLocation cx_location;
  CXCursor reference;
  CXCursor declaration;
  CXString cx_usr;
  const gchar *usr;
  g_autofree gchar *ret = NULL;
  guint line = 0;
  guint column = 0;
  enum CXLinkageKind linkage;

  g_return_val_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self), NULL);

  unit = ide_ref_ptr_get (self->native);

  file = get_file_for_location (self, location);
  line = ide_source_location_get_line (location);
  column = ide_source_location_get_line_offset (location);

  cx_location = clang_getLocation (unit, file, line + 1, column + 1);

  reference = clang_getCursor (unit, cx_location);
  declaration = clang_getCursorReferenced (reference);
  cx_usr = clang_getCursorUSR (declaration);

  linkage = clang_getCursorLinkage (declaration);

  if (linkage == CXLinkage_Internal || linkage == CXLinkage_NoLinkage)
    return NULL;

  usr = clang_getCString (cx_usr);

  if (usr == NULL)
    return NULL;

  ret = g_strdup (usr);

  clang_disposeString (cx_usr);

  return g_steal_pointer (&ret);
}