summaryrefslogtreecommitdiffstats
path: root/include/clang/Sema/SemaInternal.h
blob: 07e633cab8ee76df1bb4c17693a1940dbf6e0a17 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
//===--- SemaInternal.h - Internal Sema Interfaces --------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file provides common API and #includes for the internal
// implementation of Sema.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_SEMA_SEMAINTERNAL_H
#define LLVM_CLANG_SEMA_SEMAINTERNAL_H

#include "clang/AST/ASTContext.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaDiagnostic.h"

namespace clang {

inline PartialDiagnostic Sema::PDiag(unsigned DiagID) {
  return PartialDiagnostic(DiagID, Context.getDiagAllocator());
}

inline bool
FTIHasSingleVoidParameter(const DeclaratorChunk::FunctionTypeInfo &FTI) {
  return FTI.NumParams == 1 && !FTI.isVariadic &&
         FTI.Params[0].Ident == nullptr && FTI.Params[0].Param &&
         cast<ParmVarDecl>(FTI.Params[0].Param)->getType()->isVoidType();
}

inline bool
FTIHasNonVoidParameters(const DeclaratorChunk::FunctionTypeInfo &FTI) {
  // Assume FTI is well-formed.
  return FTI.NumParams && !FTIHasSingleVoidParameter(FTI);
}

// This requires the variable to be non-dependent and the initializer
// to not be value dependent.
inline bool IsVariableAConstantExpression(VarDecl *Var, ASTContext &Context) {
  const VarDecl *DefVD = nullptr;
  return !isa<ParmVarDecl>(Var) &&
    Var->isUsableInConstantExpressions(Context) &&
    Var->getAnyInitializer(DefVD) && DefVD->checkInitIsICE();
}

// Helper function to check whether D's attributes match current CUDA mode.
// Decls with mismatched attributes and related diagnostics may have to be
// ignored during this CUDA compilation pass.
inline bool DeclAttrsMatchCUDAMode(const LangOptions &LangOpts, Decl *D) {
  if (!LangOpts.CUDA || !D)
    return true;
  bool isDeviceSideDecl = D->hasAttr<CUDADeviceAttr>() ||
                          D->hasAttr<CUDASharedAttr>() ||
                          D->hasAttr<CUDAGlobalAttr>();
  return isDeviceSideDecl == LangOpts.CUDAIsDevice;
}

// Directly mark a variable odr-used. Given a choice, prefer to use
// MarkVariableReferenced since it does additional checks and then
// calls MarkVarDeclODRUsed.
// If the variable must be captured:
//  - if FunctionScopeIndexToStopAt is null, capture it in the CurContext
//  - else capture it in the DeclContext that maps to the
//    *FunctionScopeIndexToStopAt on the FunctionScopeInfo stack.
inline void MarkVarDeclODRUsed(VarDecl *Var,
    SourceLocation Loc, Sema &SemaRef,
    const unsigned *const FunctionScopeIndexToStopAt) {
  // Keep track of used but undefined variables.
  // FIXME: We shouldn't suppress this warning for static data members.
  if (Var->hasDefinition(SemaRef.Context) == VarDecl::DeclarationOnly &&
      (!Var->isExternallyVisible() || Var->isInline() ||
       SemaRef.isExternalWithNoLinkageType(Var)) &&
      !(Var->isStaticDataMember() && Var->hasInit())) {
    SourceLocation &old = SemaRef.UndefinedButUsed[Var->getCanonicalDecl()];
    if (old.isInvalid())
      old = Loc;
  }
  QualType CaptureType, DeclRefType;
  SemaRef.tryCaptureVariable(Var, Loc, Sema::TryCapture_Implicit,
    /*EllipsisLoc*/ SourceLocation(),
    /*BuildAndDiagnose*/ true,
    CaptureType, DeclRefType,
    FunctionScopeIndexToStopAt);

  Var->markUsed(SemaRef.Context);
}

/// Return a DLL attribute from the declaration.
inline InheritableAttr *getDLLAttr(Decl *D) {
  assert(!(D->hasAttr<DLLImportAttr>() && D->hasAttr<DLLExportAttr>()) &&
         "A declaration cannot be both dllimport and dllexport.");
  if (auto *Import = D->getAttr<DLLImportAttr>())
    return Import;
  if (auto *Export = D->getAttr<DLLExportAttr>())
    return Export;
  return nullptr;
}

/// Retrieve the depth and index of a template parameter.
inline std::pair<unsigned, unsigned> getDepthAndIndex(NamedDecl *ND) {
  if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(ND))
    return std::make_pair(TTP->getDepth(), TTP->getIndex());

  if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(ND))
    return std::make_pair(NTTP->getDepth(), NTTP->getIndex());

  const auto *TTP = cast<TemplateTemplateParmDecl>(ND);
  return std::make_pair(TTP->getDepth(), TTP->getIndex());
}

/// Retrieve the depth and index of an unexpanded parameter pack.
inline std::pair<unsigned, unsigned>
getDepthAndIndex(UnexpandedParameterPack UPP) {
  if (const auto *TTP = UPP.first.dyn_cast<const TemplateTypeParmType *>())
    return std::make_pair(TTP->getDepth(), TTP->getIndex());

  return getDepthAndIndex(UPP.first.get<NamedDecl *>());
}

class TypoCorrectionConsumer : public VisibleDeclConsumer {
  typedef SmallVector<TypoCorrection, 1> TypoResultList;
  typedef llvm::StringMap<TypoResultList> TypoResultsMap;
  typedef std::map<unsigned, TypoResultsMap> TypoEditDistanceMap;

public:
  TypoCorrectionConsumer(Sema &SemaRef,
                         const DeclarationNameInfo &TypoName,
                         Sema::LookupNameKind LookupKind,
                         Scope *S, CXXScopeSpec *SS,
                         std::unique_ptr<CorrectionCandidateCallback> CCC,
                         DeclContext *MemberContext,
                         bool EnteringContext)
      : Typo(TypoName.getName().getAsIdentifierInfo()), CurrentTCIndex(0),
        SavedTCIndex(0), SemaRef(SemaRef), S(S),
        SS(SS ? llvm::make_unique<CXXScopeSpec>(*SS) : nullptr),
        CorrectionValidator(std::move(CCC)), MemberContext(MemberContext),
        Result(SemaRef, TypoName, LookupKind),
        Namespaces(SemaRef.Context, SemaRef.CurContext, SS),
        EnteringContext(EnteringContext), SearchNamespaces(false) {
    Result.suppressDiagnostics();
    // Arrange for ValidatedCorrections[0] to always be an empty correction.
    ValidatedCorrections.push_back(TypoCorrection());
  }

  bool includeHiddenDecls() const override { return true; }

  // Methods for adding potential corrections to the consumer.
  void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, DeclContext *Ctx,
                 bool InBaseClass) override;
  void FoundName(StringRef Name);
  void addKeywordResult(StringRef Keyword);
  void addCorrection(TypoCorrection Correction);

  bool empty() const {
    return CorrectionResults.empty() && ValidatedCorrections.size() == 1;
  }

  /// Return the list of TypoCorrections for the given identifier from
  /// the set of corrections that have the closest edit distance, if any.
  TypoResultList &operator[](StringRef Name) {
    return CorrectionResults.begin()->second[Name];
  }

  /// Return the edit distance of the corrections that have the
  /// closest/best edit distance from the original typop.
  unsigned getBestEditDistance(bool Normalized) {
    if (CorrectionResults.empty())
      return (std::numeric_limits<unsigned>::max)();

    unsigned BestED = CorrectionResults.begin()->first;
    return Normalized ? TypoCorrection::NormalizeEditDistance(BestED) : BestED;
  }

  /// Set-up method to add to the consumer the set of namespaces to use
  /// in performing corrections to nested name specifiers. This method also
  /// implicitly adds all of the known classes in the current AST context to the
  /// to the consumer for correcting nested name specifiers.
  void
  addNamespaces(const llvm::MapVector<NamespaceDecl *, bool> &KnownNamespaces);

  /// Return the next typo correction that passes all internal filters
  /// and is deemed valid by the consumer's CorrectionCandidateCallback,
  /// starting with the corrections that have the closest edit distance. An
  /// empty TypoCorrection is returned once no more viable corrections remain
  /// in the consumer.
  const TypoCorrection &getNextCorrection();

  /// Get the last correction returned by getNextCorrection().
  const TypoCorrection &getCurrentCorrection() {
    return CurrentTCIndex < ValidatedCorrections.size()
               ? ValidatedCorrections[CurrentTCIndex]
               : ValidatedCorrections[0];  // The empty correction.
  }

  /// Return the next typo correction like getNextCorrection, but keep
  /// the internal state pointed to the current correction (i.e. the next time
  /// getNextCorrection is called, it will return the same correction returned
  /// by peekNextcorrection).
  const TypoCorrection &peekNextCorrection() {
    auto Current = CurrentTCIndex;
    const TypoCorrection &TC = getNextCorrection();
    CurrentTCIndex = Current;
    return TC;
  }

  /// Reset the consumer's position in the stream of viable corrections
  /// (i.e. getNextCorrection() will return each of the previously returned
  /// corrections in order before returning any new corrections).
  void resetCorrectionStream() {
    CurrentTCIndex = 0;
  }

  /// Return whether the end of the stream of corrections has been
  /// reached.
  bool finished() {
    return CorrectionResults.empty() &&
           CurrentTCIndex >= ValidatedCorrections.size();
  }

  /// Save the current position in the correction stream (overwriting any
  /// previously saved position).
  void saveCurrentPosition() {
    SavedTCIndex = CurrentTCIndex;
  }

  /// Restore the saved position in the correction stream.
  void restoreSavedPosition() {
    CurrentTCIndex = SavedTCIndex;
  }

  ASTContext &getContext() const { return SemaRef.Context; }
  const LookupResult &getLookupResult() const { return Result; }

  bool isAddressOfOperand() const { return CorrectionValidator->IsAddressOfOperand; }
  const CXXScopeSpec *getSS() const { return SS.get(); }
  Scope *getScope() const { return S; }
  CorrectionCandidateCallback *getCorrectionValidator() const {
    return CorrectionValidator.get();
  }

private:
  class NamespaceSpecifierSet {
    struct SpecifierInfo {
      DeclContext* DeclCtx;
      NestedNameSpecifier* NameSpecifier;
      unsigned EditDistance;
    };

    typedef SmallVector<DeclContext*, 4> DeclContextList;
    typedef SmallVector<SpecifierInfo, 16> SpecifierInfoList;

    ASTContext &Context;
    DeclContextList CurContextChain;
    std::string CurNameSpecifier;
    SmallVector<const IdentifierInfo*, 4> CurContextIdentifiers;
    SmallVector<const IdentifierInfo*, 4> CurNameSpecifierIdentifiers;

    std::map<unsigned, SpecifierInfoList> DistanceMap;

    /// Helper for building the list of DeclContexts between the current
    /// context and the top of the translation unit
    static DeclContextList buildContextChain(DeclContext *Start);

    unsigned buildNestedNameSpecifier(DeclContextList &DeclChain,
                                      NestedNameSpecifier *&NNS);

   public:
    NamespaceSpecifierSet(ASTContext &Context, DeclContext *CurContext,
                          CXXScopeSpec *CurScopeSpec);

    /// Add the DeclContext (a namespace or record) to the set, computing
    /// the corresponding NestedNameSpecifier and its distance in the process.
    void addNameSpecifier(DeclContext *Ctx);

    /// Provides flat iteration over specifiers, sorted by distance.
    class iterator
        : public llvm::iterator_facade_base<iterator, std::forward_iterator_tag,
                                            SpecifierInfo> {
      /// Always points to the last element in the distance map.
      const std::map<unsigned, SpecifierInfoList>::iterator OuterBack;
      /// Iterator on the distance map.
      std::map<unsigned, SpecifierInfoList>::iterator Outer;
      /// Iterator on an element in the distance map.
      SpecifierInfoList::iterator Inner;

    public:
      iterator(NamespaceSpecifierSet &Set, bool IsAtEnd)
          : OuterBack(std::prev(Set.DistanceMap.end())),
            Outer(Set.DistanceMap.begin()),
            Inner(!IsAtEnd ? Outer->second.begin() : OuterBack->second.end()) {
        assert(!Set.DistanceMap.empty());
      }

      iterator &operator++() {
        ++Inner;
        if (Inner == Outer->second.end() && Outer != OuterBack) {
          ++Outer;
          Inner = Outer->second.begin();
        }
        return *this;
      }

      SpecifierInfo &operator*() { return *Inner; }
      bool operator==(const iterator &RHS) const { return Inner == RHS.Inner; }
    };

    iterator begin() { return iterator(*this, /*IsAtEnd=*/false); }
    iterator end() { return iterator(*this, /*IsAtEnd=*/true); }
  };

  void addName(StringRef Name, NamedDecl *ND,
               NestedNameSpecifier *NNS = nullptr, bool isKeyword = false);

  /// Find any visible decls for the given typo correction candidate.
  /// If none are found, it to the set of candidates for which qualified lookups
  /// will be performed to find possible nested name specifier changes.
  bool resolveCorrection(TypoCorrection &Candidate);

  /// Perform qualified lookups on the queued set of typo correction
  /// candidates and add the nested name specifier changes to each candidate if
  /// a lookup succeeds (at which point the candidate will be returned to the
  /// main pool of potential corrections).
  void performQualifiedLookups();

  /// The name written that is a typo in the source.
  IdentifierInfo *Typo;

  /// The results found that have the smallest edit distance
  /// found (so far) with the typo name.
  ///
  /// The pointer value being set to the current DeclContext indicates
  /// whether there is a keyword with this name.
  TypoEditDistanceMap CorrectionResults;

  SmallVector<TypoCorrection, 4> ValidatedCorrections;
  size_t CurrentTCIndex;
  size_t SavedTCIndex;

  Sema &SemaRef;
  Scope *S;
  std::unique_ptr<CXXScopeSpec> SS;
  std::unique_ptr<CorrectionCandidateCallback> CorrectionValidator;
  DeclContext *MemberContext;
  LookupResult Result;
  NamespaceSpecifierSet Namespaces;
  SmallVector<TypoCorrection, 2> QualifiedResults;
  bool EnteringContext;
  bool SearchNamespaces;
};

inline Sema::TypoExprState::TypoExprState() {}

inline Sema::TypoExprState::TypoExprState(TypoExprState &&other) noexcept {
  *this = std::move(other);
}

inline Sema::TypoExprState &Sema::TypoExprState::
operator=(Sema::TypoExprState &&other) noexcept {
  Consumer = std::move(other.Consumer);
  DiagHandler = std::move(other.DiagHandler);
  RecoveryHandler = std::move(other.RecoveryHandler);
  return *this;
}

} // end namespace clang

#endif