Home | History | Annotate | Line # | Download | only in Lex
      1 //===--- MultipleIncludeOpt.h - Header Multiple-Include Optzn ---*- C++ -*-===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 ///
      9 /// \file
     10 /// Defines the MultipleIncludeOpt interface.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #ifndef LLVM_CLANG_LEX_MULTIPLEINCLUDEOPT_H
     15 #define LLVM_CLANG_LEX_MULTIPLEINCLUDEOPT_H
     16 
     17 #include "clang/Basic/SourceLocation.h"
     18 
     19 namespace clang {
     20 class IdentifierInfo;
     21 
     22 /// Implements the simple state machine that the Lexer class uses to
     23 /// detect files subject to the 'multiple-include' optimization.
     24 ///
     25 /// The public methods in this class are triggered by various
     26 /// events that occur when a file is lexed, and after the entire file is lexed,
     27 /// information about which macro (if any) controls the header is returned.
     28 class MultipleIncludeOpt {
     29   /// ReadAnyTokens - This is set to false when a file is first opened and true
     30   /// any time a token is returned to the client or a (non-multiple-include)
     31   /// directive is parsed.  When the final \#endif is parsed this is reset back
     32   /// to false, that way any tokens before the first \#ifdef or after the last
     33   /// \#endif can be easily detected.
     34   bool ReadAnyTokens;
     35 
     36   /// ImmediatelyAfterTopLevelIfndef - This is true when the only tokens
     37   /// processed in the file so far is an #ifndef and an identifier.  Used in
     38   /// the detection of header guards in a file.
     39   bool ImmediatelyAfterTopLevelIfndef;
     40 
     41   /// ReadAnyTokens - This is set to false when a file is first opened and true
     42   /// any time a token is returned to the client or a (non-multiple-include)
     43   /// directive is parsed.  When the final #endif is parsed this is reset back
     44   /// to false, that way any tokens before the first #ifdef or after the last
     45   /// #endif can be easily detected.
     46   bool DidMacroExpansion;
     47 
     48   /// TheMacro - The controlling macro for a file, if valid.
     49   ///
     50   const IdentifierInfo *TheMacro;
     51 
     52   /// DefinedMacro - The macro defined right after TheMacro, if any.
     53   const IdentifierInfo *DefinedMacro;
     54 
     55   SourceLocation MacroLoc;
     56   SourceLocation DefinedLoc;
     57 public:
     58   MultipleIncludeOpt() {
     59     ReadAnyTokens = false;
     60     ImmediatelyAfterTopLevelIfndef = false;
     61     DidMacroExpansion = false;
     62     TheMacro = nullptr;
     63     DefinedMacro = nullptr;
     64   }
     65 
     66   SourceLocation GetMacroLocation() const {
     67     return MacroLoc;
     68   }
     69 
     70   SourceLocation GetDefinedLocation() const {
     71     return DefinedLoc;
     72   }
     73 
     74   void resetImmediatelyAfterTopLevelIfndef() {
     75     ImmediatelyAfterTopLevelIfndef = false;
     76   }
     77 
     78   void SetDefinedMacro(IdentifierInfo *M, SourceLocation Loc) {
     79     DefinedMacro = M;
     80     DefinedLoc = Loc;
     81   }
     82 
     83   /// Invalidate - Permanently mark this file as not being suitable for the
     84   /// include-file optimization.
     85   void Invalidate() {
     86     // If we have read tokens but have no controlling macro, the state-machine
     87     // below can never "accept".
     88     ReadAnyTokens = true;
     89     ImmediatelyAfterTopLevelIfndef = false;
     90     DefinedMacro = nullptr;
     91     TheMacro = nullptr;
     92   }
     93 
     94   /// getHasReadAnyTokensVal - This is used for the \#ifndef handshake at the
     95   /// top of the file when reading preprocessor directives.  Otherwise, reading
     96   /// the "ifndef x" would count as reading tokens.
     97   bool getHasReadAnyTokensVal() const { return ReadAnyTokens; }
     98 
     99   /// getImmediatelyAfterTopLevelIfndef - returns true if the last directive
    100   /// was an #ifndef at the beginning of the file.
    101   bool getImmediatelyAfterTopLevelIfndef() const {
    102     return ImmediatelyAfterTopLevelIfndef;
    103   }
    104 
    105   // If a token is read, remember that we have seen a side-effect in this file.
    106   void ReadToken() {
    107     ReadAnyTokens = true;
    108     ImmediatelyAfterTopLevelIfndef = false;
    109   }
    110 
    111   /// ExpandedMacro - When a macro is expanded with this lexer as the current
    112   /// buffer, this method is called to disable the MIOpt if needed.
    113   void ExpandedMacro() { DidMacroExpansion = true; }
    114 
    115   /// Called when entering a top-level \#ifndef directive (or the
    116   /// "\#if !defined" equivalent) without any preceding tokens.
    117   ///
    118   /// Note, we don't care about the input value of 'ReadAnyTokens'.  The caller
    119   /// ensures that this is only called if there are no tokens read before the
    120   /// \#ifndef.  The caller is required to do this, because reading the \#if
    121   /// line obviously reads in tokens.
    122   void EnterTopLevelIfndef(const IdentifierInfo *M, SourceLocation Loc) {
    123     // If the macro is already set, this is after the top-level #endif.
    124     if (TheMacro)
    125       return Invalidate();
    126 
    127     // If we have already expanded a macro by the end of the #ifndef line, then
    128     // there is a macro expansion *in* the #ifndef line.  This means that the
    129     // condition could evaluate differently when subsequently #included.  Reject
    130     // this.
    131     if (DidMacroExpansion)
    132       return Invalidate();
    133 
    134     // Remember that we're in the #if and that we have the macro.
    135     ReadAnyTokens = true;
    136     ImmediatelyAfterTopLevelIfndef = true;
    137     TheMacro = M;
    138     MacroLoc = Loc;
    139   }
    140 
    141   /// Invoked when a top level conditional (except \#ifndef) is found.
    142   void EnterTopLevelConditional() {
    143     // If a conditional directive (except #ifndef) is found at the top level,
    144     // there is a chunk of the file not guarded by the controlling macro.
    145     Invalidate();
    146   }
    147 
    148   /// Called when the lexer exits the top-level conditional.
    149   void ExitTopLevelConditional() {
    150     // If we have a macro, that means the top of the file was ok.  Set our state
    151     // back to "not having read any tokens" so we can detect anything after the
    152     // #endif.
    153     if (!TheMacro) return Invalidate();
    154 
    155     // At this point, we haven't "read any tokens" but we do have a controlling
    156     // macro.
    157     ReadAnyTokens = false;
    158     ImmediatelyAfterTopLevelIfndef = false;
    159   }
    160 
    161   /// Once the entire file has been lexed, if there is a controlling
    162   /// macro, return it.
    163   const IdentifierInfo *GetControllingMacroAtEndOfFile() const {
    164     // If we haven't read any tokens after the #endif, return the controlling
    165     // macro if it's valid (if it isn't, it will be null).
    166     if (!ReadAnyTokens)
    167       return TheMacro;
    168     return nullptr;
    169   }
    170 
    171   /// If the ControllingMacro is followed by a macro definition, return
    172   /// the macro that was defined.
    173   const IdentifierInfo *GetDefinedMacro() const {
    174     return DefinedMacro;
    175   }
    176 };
    177 
    178 }  // end namespace clang
    179 
    180 #endif
    181