crytic_compile.compilation_unit

Module handling the compilation unit Each compilation unit represents a call to the compiler Each compilation unit has one or more source units associated with it At least one compilation unit exists for each version of solc used Maybe more dependending on the framework used (hardhat/foundry/etc)

  1"""
  2Module handling the compilation unit
  3Each compilation unit represents a call to the compiler
  4Each compilation unit has one or more source units associated with it
  5At least one compilation unit exists for each version of solc used
  6    Maybe more dependending on the framework used (hardhat/foundry/etc)
  7"""
  8import uuid
  9from collections import defaultdict
 10from typing import TYPE_CHECKING, Dict, List, Set, Optional
 11
 12from crytic_compile.compiler.compiler import CompilerVersion
 13from crytic_compile.source_unit import SourceUnit
 14from crytic_compile.utils.naming import Filename
 15
 16# Cycle dependency
 17if TYPE_CHECKING:
 18    from crytic_compile import CryticCompile
 19
 20# pylint: disable=too-many-instance-attributes
 21class CompilationUnit:
 22    """CompilationUnit class"""
 23
 24    def __init__(self, crytic_compile: "CryticCompile", unique_id: str):
 25        """Init the object
 26
 27        Args:
 28            crytic_compile (CryticCompile): Associated CryticCompile object
 29            unique_id (str): Unique ID used to identify the compilation unit
 30        """
 31
 32        # mapping from filename to contract name
 33        self._filename_to_contracts: Dict[Filename, Set[str]] = defaultdict(set)
 34
 35        # mapping from filename to source unit
 36        self._source_units: Dict[Filename, SourceUnit] = {}
 37
 38        # set containing all the filenames of this compilation unit
 39        self._filenames: List[Filename] = []
 40
 41        # mapping from absolute/relative/used to filename
 42        self._filenames_lookup: Optional[Dict[str, Filename]] = None
 43
 44        # compiler.compiler
 45        self._compiler_version: CompilerVersion = CompilerVersion(
 46            compiler="N/A", version="N/A", optimized=False
 47        )
 48
 49        # if the compilation unit comes from etherscan-like service and is a proxy,
 50        # store the implementation address
 51        self._implementation_address: Optional[str] = None
 52
 53        self._crytic_compile: "CryticCompile" = crytic_compile
 54
 55        if unique_id == ".":
 56            unique_id = str(uuid.uuid4())
 57
 58        crytic_compile.compilation_units[unique_id] = self  # type: ignore
 59
 60        self._unique_id = unique_id
 61
 62    @property
 63    def unique_id(self) -> str:
 64        """Return the compilation unit ID
 65
 66        Returns:
 67            str: Compilation unit unique ID
 68        """
 69        return self._unique_id
 70
 71    @property
 72    def crytic_compile(self) -> "CryticCompile":
 73        """Return the CryticCompile object
 74
 75        Returns:
 76            CryticCompile: Associated CryticCompile object
 77        """
 78        return self._crytic_compile
 79
 80    @property
 81    def source_units(self) -> Dict[Filename, SourceUnit]:
 82        """
 83        Return the dict of the source units
 84
 85        Returns:
 86            Dict[Filename, SourceUnit]: the source units
 87        """
 88        return self._source_units
 89
 90    def source_unit(self, filename: Filename) -> SourceUnit:
 91        """
 92        Return the source unit associated to the filename.
 93        The source unit must have been created by create_source_units
 94
 95        Args:
 96            filename: filename of the source unit
 97
 98        Returns:
 99            SourceUnit: the source unit
100        """
101        return self._source_units[filename]
102
103    @property
104    def asts(self) -> Dict[str, Dict]:
105        """
106        Return all the asts from the compilation unit
107
108        Returns:
109            Dict[str, Dict]: absolute path -> ast
110        """
111        return {
112            source_unit.filename.absolute: source_unit.ast
113            for source_unit in self.source_units.values()
114        }
115
116    def create_source_unit(self, filename: Filename) -> SourceUnit:
117        """
118        Create the source unit associated with the filename
119        Add the relevant info in the compilation unit/crytic compile
120        If the source unit already exist, return it
121        Also appends filename to the end of filenames, if not already present
122        So this function should be called in the order you want filenames to have
123
124        Args:
125            filename (Filename): filename of the source unit
126
127        Returns:
128            SourceUnit: the source unit
129        """
130        if not filename in self._source_units:
131            source_unit = SourceUnit(self, filename)  # type: ignore
132            self._source_units[filename] = source_unit
133            if filename not in self.filenames:
134                self.filenames.append(filename)
135        return self._source_units[filename]
136
137    @property
138    def implementation_address(self) -> Optional[str]:
139        """Return the implementation address if the compilation unit is a proxy
140
141        Returns:
142            Optional[str]: Implementation address
143        """
144        return self._implementation_address
145
146    @implementation_address.setter
147    def implementation_address(self, implementation: str) -> None:
148        """Set the implementation address
149
150        Args:
151            implementation (str): Implementation address
152        """
153        self._implementation_address = implementation
154
155    # endregion
156    ###################################################################################
157    ###################################################################################
158    # region Filenames
159    ###################################################################################
160    ###################################################################################
161
162    @property
163    def filenames(self) -> List[Filename]:
164        """Return the filenames used by the compilation unit
165
166        Returns:
167            list[Filename]: Filenames used by the compilation units
168        """
169        return self._filenames
170
171    @filenames.setter
172    def filenames(self, all_filenames: List[Filename]) -> None:
173        """Set the filenames
174
175        Args:
176            all_filenames (List[Filename]): new filenames
177        """
178        self._filenames = all_filenames
179
180    @property
181    def filename_to_contracts(self) -> Dict[Filename, Set[str]]:
182        """Return a dict mapping the filename to a list of contract declared
183
184        Returns:
185            Dict[Filename, List[str]]: Filename -> List[contract_name]
186        """
187        return self._filename_to_contracts
188
189    def find_absolute_filename_from_used_filename(self, used_filename: str) -> str:
190        """Return the absolute filename based on the used one
191
192        Args:
193            used_filename (str): Used filename
194
195        Raises:
196            ValueError: If the filename is not found
197
198        Returns:
199            str: Absolute filename
200        """
201        # Note: we could memoize this function if the third party end up using it heavily
202        # If used_filename is already an absolute path no need to lookup
203        if used_filename in self._crytic_compile.filenames:
204            return used_filename
205        d_file = {f.used: f.absolute for f in self._filenames}
206        if used_filename not in d_file:
207            raise ValueError("f{filename} does not exist in {d}")
208        return d_file[used_filename]
209
210    def relative_filename_from_absolute_filename(self, absolute_filename: str) -> str:
211        """Return the relative file based on the absolute name
212
213        Args:
214            absolute_filename (str): Absolute filename
215
216        Raises:
217            ValueError: If the filename is not found
218
219        Returns:
220            str: Absolute filename
221        """
222        d_file = {f.absolute: f.relative for f in self._filenames}
223        if absolute_filename not in d_file:
224            raise ValueError("f{absolute_filename} does not exist in {d}")
225        return d_file[absolute_filename]
226
227    def filename_lookup(self, filename: str) -> Filename:
228        """Return a crytic_compile.naming.Filename from a any filename
229
230        Args:
231            filename (str): filename (used/absolute/relative)
232
233        Raises:
234            ValueError: If the filename is not in the project
235
236        Returns:
237            Filename: Associated Filename object
238        """
239        # pylint: disable=import-outside-toplevel
240        from crytic_compile.platform.truffle import Truffle
241
242        if isinstance(self.crytic_compile.platform, Truffle) and filename.startswith("project:/"):
243            filename = filename[len("project:/") :]
244
245        if self._filenames_lookup is None:
246            self._filenames_lookup = {}
247            for file in self._filenames:
248                self._filenames_lookup[file.absolute] = file
249                self._filenames_lookup[file.relative] = file
250                self._filenames_lookup[file.used] = file
251        if filename not in self._filenames_lookup:
252            raise ValueError(
253                f"{filename} does not exist in {[f.absolute for f in self._filenames_lookup.values()]}"
254            )
255        return self._filenames_lookup[filename]
256
257    # endregion
258    ###################################################################################
259    ###################################################################################
260    # region Compiler information
261    ###################################################################################
262    ###################################################################################
263
264    @property
265    def compiler_version(self) -> "CompilerVersion":
266        """Return the compiler info
267
268        Returns:
269            CompilerVersion: compiler info
270        """
271        return self._compiler_version
272
273    @compiler_version.setter
274    def compiler_version(self, compiler: CompilerVersion) -> None:
275        """Set the compiler version
276
277        Args:
278            compiler (CompilerVersion): New compiler version
279        """
280        self._compiler_version = compiler
281
282    # endregion
283    ###################################################################################
284    ###################################################################################
class CompilationUnit:
 22class CompilationUnit:
 23    """CompilationUnit class"""
 24
 25    def __init__(self, crytic_compile: "CryticCompile", unique_id: str):
 26        """Init the object
 27
 28        Args:
 29            crytic_compile (CryticCompile): Associated CryticCompile object
 30            unique_id (str): Unique ID used to identify the compilation unit
 31        """
 32
 33        # mapping from filename to contract name
 34        self._filename_to_contracts: Dict[Filename, Set[str]] = defaultdict(set)
 35
 36        # mapping from filename to source unit
 37        self._source_units: Dict[Filename, SourceUnit] = {}
 38
 39        # set containing all the filenames of this compilation unit
 40        self._filenames: List[Filename] = []
 41
 42        # mapping from absolute/relative/used to filename
 43        self._filenames_lookup: Optional[Dict[str, Filename]] = None
 44
 45        # compiler.compiler
 46        self._compiler_version: CompilerVersion = CompilerVersion(
 47            compiler="N/A", version="N/A", optimized=False
 48        )
 49
 50        # if the compilation unit comes from etherscan-like service and is a proxy,
 51        # store the implementation address
 52        self._implementation_address: Optional[str] = None
 53
 54        self._crytic_compile: "CryticCompile" = crytic_compile
 55
 56        if unique_id == ".":
 57            unique_id = str(uuid.uuid4())
 58
 59        crytic_compile.compilation_units[unique_id] = self  # type: ignore
 60
 61        self._unique_id = unique_id
 62
 63    @property
 64    def unique_id(self) -> str:
 65        """Return the compilation unit ID
 66
 67        Returns:
 68            str: Compilation unit unique ID
 69        """
 70        return self._unique_id
 71
 72    @property
 73    def crytic_compile(self) -> "CryticCompile":
 74        """Return the CryticCompile object
 75
 76        Returns:
 77            CryticCompile: Associated CryticCompile object
 78        """
 79        return self._crytic_compile
 80
 81    @property
 82    def source_units(self) -> Dict[Filename, SourceUnit]:
 83        """
 84        Return the dict of the source units
 85
 86        Returns:
 87            Dict[Filename, SourceUnit]: the source units
 88        """
 89        return self._source_units
 90
 91    def source_unit(self, filename: Filename) -> SourceUnit:
 92        """
 93        Return the source unit associated to the filename.
 94        The source unit must have been created by create_source_units
 95
 96        Args:
 97            filename: filename of the source unit
 98
 99        Returns:
100            SourceUnit: the source unit
101        """
102        return self._source_units[filename]
103
104    @property
105    def asts(self) -> Dict[str, Dict]:
106        """
107        Return all the asts from the compilation unit
108
109        Returns:
110            Dict[str, Dict]: absolute path -> ast
111        """
112        return {
113            source_unit.filename.absolute: source_unit.ast
114            for source_unit in self.source_units.values()
115        }
116
117    def create_source_unit(self, filename: Filename) -> SourceUnit:
118        """
119        Create the source unit associated with the filename
120        Add the relevant info in the compilation unit/crytic compile
121        If the source unit already exist, return it
122        Also appends filename to the end of filenames, if not already present
123        So this function should be called in the order you want filenames to have
124
125        Args:
126            filename (Filename): filename of the source unit
127
128        Returns:
129            SourceUnit: the source unit
130        """
131        if not filename in self._source_units:
132            source_unit = SourceUnit(self, filename)  # type: ignore
133            self._source_units[filename] = source_unit
134            if filename not in self.filenames:
135                self.filenames.append(filename)
136        return self._source_units[filename]
137
138    @property
139    def implementation_address(self) -> Optional[str]:
140        """Return the implementation address if the compilation unit is a proxy
141
142        Returns:
143            Optional[str]: Implementation address
144        """
145        return self._implementation_address
146
147    @implementation_address.setter
148    def implementation_address(self, implementation: str) -> None:
149        """Set the implementation address
150
151        Args:
152            implementation (str): Implementation address
153        """
154        self._implementation_address = implementation
155
156    # endregion
157    ###################################################################################
158    ###################################################################################
159    # region Filenames
160    ###################################################################################
161    ###################################################################################
162
163    @property
164    def filenames(self) -> List[Filename]:
165        """Return the filenames used by the compilation unit
166
167        Returns:
168            list[Filename]: Filenames used by the compilation units
169        """
170        return self._filenames
171
172    @filenames.setter
173    def filenames(self, all_filenames: List[Filename]) -> None:
174        """Set the filenames
175
176        Args:
177            all_filenames (List[Filename]): new filenames
178        """
179        self._filenames = all_filenames
180
181    @property
182    def filename_to_contracts(self) -> Dict[Filename, Set[str]]:
183        """Return a dict mapping the filename to a list of contract declared
184
185        Returns:
186            Dict[Filename, List[str]]: Filename -> List[contract_name]
187        """
188        return self._filename_to_contracts
189
190    def find_absolute_filename_from_used_filename(self, used_filename: str) -> str:
191        """Return the absolute filename based on the used one
192
193        Args:
194            used_filename (str): Used filename
195
196        Raises:
197            ValueError: If the filename is not found
198
199        Returns:
200            str: Absolute filename
201        """
202        # Note: we could memoize this function if the third party end up using it heavily
203        # If used_filename is already an absolute path no need to lookup
204        if used_filename in self._crytic_compile.filenames:
205            return used_filename
206        d_file = {f.used: f.absolute for f in self._filenames}
207        if used_filename not in d_file:
208            raise ValueError("f{filename} does not exist in {d}")
209        return d_file[used_filename]
210
211    def relative_filename_from_absolute_filename(self, absolute_filename: str) -> str:
212        """Return the relative file based on the absolute name
213
214        Args:
215            absolute_filename (str): Absolute filename
216
217        Raises:
218            ValueError: If the filename is not found
219
220        Returns:
221            str: Absolute filename
222        """
223        d_file = {f.absolute: f.relative for f in self._filenames}
224        if absolute_filename not in d_file:
225            raise ValueError("f{absolute_filename} does not exist in {d}")
226        return d_file[absolute_filename]
227
228    def filename_lookup(self, filename: str) -> Filename:
229        """Return a crytic_compile.naming.Filename from a any filename
230
231        Args:
232            filename (str): filename (used/absolute/relative)
233
234        Raises:
235            ValueError: If the filename is not in the project
236
237        Returns:
238            Filename: Associated Filename object
239        """
240        # pylint: disable=import-outside-toplevel
241        from crytic_compile.platform.truffle import Truffle
242
243        if isinstance(self.crytic_compile.platform, Truffle) and filename.startswith("project:/"):
244            filename = filename[len("project:/") :]
245
246        if self._filenames_lookup is None:
247            self._filenames_lookup = {}
248            for file in self._filenames:
249                self._filenames_lookup[file.absolute] = file
250                self._filenames_lookup[file.relative] = file
251                self._filenames_lookup[file.used] = file
252        if filename not in self._filenames_lookup:
253            raise ValueError(
254                f"{filename} does not exist in {[f.absolute for f in self._filenames_lookup.values()]}"
255            )
256        return self._filenames_lookup[filename]
257
258    # endregion
259    ###################################################################################
260    ###################################################################################
261    # region Compiler information
262    ###################################################################################
263    ###################################################################################
264
265    @property
266    def compiler_version(self) -> "CompilerVersion":
267        """Return the compiler info
268
269        Returns:
270            CompilerVersion: compiler info
271        """
272        return self._compiler_version
273
274    @compiler_version.setter
275    def compiler_version(self, compiler: CompilerVersion) -> None:
276        """Set the compiler version
277
278        Args:
279            compiler (CompilerVersion): New compiler version
280        """
281        self._compiler_version = compiler
282
283    # endregion
284    ###################################################################################
285    ###################################################################################

CompilationUnit class

CompilationUnit( crytic_compile: crytic_compile.crytic_compile.CryticCompile, unique_id: str)
25    def __init__(self, crytic_compile: "CryticCompile", unique_id: str):
26        """Init the object
27
28        Args:
29            crytic_compile (CryticCompile): Associated CryticCompile object
30            unique_id (str): Unique ID used to identify the compilation unit
31        """
32
33        # mapping from filename to contract name
34        self._filename_to_contracts: Dict[Filename, Set[str]] = defaultdict(set)
35
36        # mapping from filename to source unit
37        self._source_units: Dict[Filename, SourceUnit] = {}
38
39        # set containing all the filenames of this compilation unit
40        self._filenames: List[Filename] = []
41
42        # mapping from absolute/relative/used to filename
43        self._filenames_lookup: Optional[Dict[str, Filename]] = None
44
45        # compiler.compiler
46        self._compiler_version: CompilerVersion = CompilerVersion(
47            compiler="N/A", version="N/A", optimized=False
48        )
49
50        # if the compilation unit comes from etherscan-like service and is a proxy,
51        # store the implementation address
52        self._implementation_address: Optional[str] = None
53
54        self._crytic_compile: "CryticCompile" = crytic_compile
55
56        if unique_id == ".":
57            unique_id = str(uuid.uuid4())
58
59        crytic_compile.compilation_units[unique_id] = self  # type: ignore
60
61        self._unique_id = unique_id

Init the object

Args: crytic_compile (CryticCompile): Associated CryticCompile object unique_id (str): Unique ID used to identify the compilation unit

unique_id: str
63    @property
64    def unique_id(self) -> str:
65        """Return the compilation unit ID
66
67        Returns:
68            str: Compilation unit unique ID
69        """
70        return self._unique_id

Return the compilation unit ID

Returns: str: Compilation unit unique ID

crytic_compile: crytic_compile.crytic_compile.CryticCompile
72    @property
73    def crytic_compile(self) -> "CryticCompile":
74        """Return the CryticCompile object
75
76        Returns:
77            CryticCompile: Associated CryticCompile object
78        """
79        return self._crytic_compile

Return the CryticCompile object

Returns: CryticCompile: Associated CryticCompile object

81    @property
82    def source_units(self) -> Dict[Filename, SourceUnit]:
83        """
84        Return the dict of the source units
85
86        Returns:
87            Dict[Filename, SourceUnit]: the source units
88        """
89        return self._source_units

Return the dict of the source units

Returns: Dict[Filename, SourceUnit]: the source units

def source_unit( self, filename: crytic_compile.utils.naming.Filename) -> crytic_compile.source_unit.SourceUnit:
 91    def source_unit(self, filename: Filename) -> SourceUnit:
 92        """
 93        Return the source unit associated to the filename.
 94        The source unit must have been created by create_source_units
 95
 96        Args:
 97            filename: filename of the source unit
 98
 99        Returns:
100            SourceUnit: the source unit
101        """
102        return self._source_units[filename]

Return the source unit associated to the filename. The source unit must have been created by create_source_units

Args: filename: filename of the source unit

Returns: SourceUnit: the source unit

asts: Dict[str, Dict]
104    @property
105    def asts(self) -> Dict[str, Dict]:
106        """
107        Return all the asts from the compilation unit
108
109        Returns:
110            Dict[str, Dict]: absolute path -> ast
111        """
112        return {
113            source_unit.filename.absolute: source_unit.ast
114            for source_unit in self.source_units.values()
115        }

Return all the asts from the compilation unit

Returns: Dict[str, Dict]: absolute path -> ast

def create_source_unit( self, filename: crytic_compile.utils.naming.Filename) -> crytic_compile.source_unit.SourceUnit:
117    def create_source_unit(self, filename: Filename) -> SourceUnit:
118        """
119        Create the source unit associated with the filename
120        Add the relevant info in the compilation unit/crytic compile
121        If the source unit already exist, return it
122        Also appends filename to the end of filenames, if not already present
123        So this function should be called in the order you want filenames to have
124
125        Args:
126            filename (Filename): filename of the source unit
127
128        Returns:
129            SourceUnit: the source unit
130        """
131        if not filename in self._source_units:
132            source_unit = SourceUnit(self, filename)  # type: ignore
133            self._source_units[filename] = source_unit
134            if filename not in self.filenames:
135                self.filenames.append(filename)
136        return self._source_units[filename]

Create the source unit associated with the filename Add the relevant info in the compilation unit/crytic compile If the source unit already exist, return it Also appends filename to the end of filenames, if not already present So this function should be called in the order you want filenames to have

Args: filename (Filename): filename of the source unit

Returns: SourceUnit: the source unit

implementation_address: Union[str, NoneType]
138    @property
139    def implementation_address(self) -> Optional[str]:
140        """Return the implementation address if the compilation unit is a proxy
141
142        Returns:
143            Optional[str]: Implementation address
144        """
145        return self._implementation_address

Return the implementation address if the compilation unit is a proxy

Returns: Optional[str]: Implementation address

filenames: List[crytic_compile.utils.naming.Filename]
163    @property
164    def filenames(self) -> List[Filename]:
165        """Return the filenames used by the compilation unit
166
167        Returns:
168            list[Filename]: Filenames used by the compilation units
169        """
170        return self._filenames

Return the filenames used by the compilation unit

Returns: list[Filename]: Filenames used by the compilation units

filename_to_contracts: Dict[crytic_compile.utils.naming.Filename, Set[str]]
181    @property
182    def filename_to_contracts(self) -> Dict[Filename, Set[str]]:
183        """Return a dict mapping the filename to a list of contract declared
184
185        Returns:
186            Dict[Filename, List[str]]: Filename -> List[contract_name]
187        """
188        return self._filename_to_contracts

Return a dict mapping the filename to a list of contract declared

Returns: Dict[Filename, List[str]]: Filename -> List[contract_name]

def find_absolute_filename_from_used_filename(self, used_filename: str) -> str:
190    def find_absolute_filename_from_used_filename(self, used_filename: str) -> str:
191        """Return the absolute filename based on the used one
192
193        Args:
194            used_filename (str): Used filename
195
196        Raises:
197            ValueError: If the filename is not found
198
199        Returns:
200            str: Absolute filename
201        """
202        # Note: we could memoize this function if the third party end up using it heavily
203        # If used_filename is already an absolute path no need to lookup
204        if used_filename in self._crytic_compile.filenames:
205            return used_filename
206        d_file = {f.used: f.absolute for f in self._filenames}
207        if used_filename not in d_file:
208            raise ValueError("f{filename} does not exist in {d}")
209        return d_file[used_filename]

Return the absolute filename based on the used one

Args: used_filename (str): Used filename

Raises: ValueError: If the filename is not found

Returns: str: Absolute filename

def relative_filename_from_absolute_filename(self, absolute_filename: str) -> str:
211    def relative_filename_from_absolute_filename(self, absolute_filename: str) -> str:
212        """Return the relative file based on the absolute name
213
214        Args:
215            absolute_filename (str): Absolute filename
216
217        Raises:
218            ValueError: If the filename is not found
219
220        Returns:
221            str: Absolute filename
222        """
223        d_file = {f.absolute: f.relative for f in self._filenames}
224        if absolute_filename not in d_file:
225            raise ValueError("f{absolute_filename} does not exist in {d}")
226        return d_file[absolute_filename]

Return the relative file based on the absolute name

Args: absolute_filename (str): Absolute filename

Raises: ValueError: If the filename is not found

Returns: str: Absolute filename

def filename_lookup(self, filename: str) -> crytic_compile.utils.naming.Filename:
228    def filename_lookup(self, filename: str) -> Filename:
229        """Return a crytic_compile.naming.Filename from a any filename
230
231        Args:
232            filename (str): filename (used/absolute/relative)
233
234        Raises:
235            ValueError: If the filename is not in the project
236
237        Returns:
238            Filename: Associated Filename object
239        """
240        # pylint: disable=import-outside-toplevel
241        from crytic_compile.platform.truffle import Truffle
242
243        if isinstance(self.crytic_compile.platform, Truffle) and filename.startswith("project:/"):
244            filename = filename[len("project:/") :]
245
246        if self._filenames_lookup is None:
247            self._filenames_lookup = {}
248            for file in self._filenames:
249                self._filenames_lookup[file.absolute] = file
250                self._filenames_lookup[file.relative] = file
251                self._filenames_lookup[file.used] = file
252        if filename not in self._filenames_lookup:
253            raise ValueError(
254                f"{filename} does not exist in {[f.absolute for f in self._filenames_lookup.values()]}"
255            )
256        return self._filenames_lookup[filename]

Return a crytic_compile.naming.Filename from a any filename

Args: filename (str): filename (used/absolute/relative)

Raises: ValueError: If the filename is not in the project

Returns: Filename: Associated Filename object

compiler_version: crytic_compile.compiler.compiler.CompilerVersion
265    @property
266    def compiler_version(self) -> "CompilerVersion":
267        """Return the compiler info
268
269        Returns:
270            CompilerVersion: compiler info
271        """
272        return self._compiler_version

Return the compiler info

Returns: CompilerVersion: compiler info