vhdl_lang/named_entity/
visibility.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this file,
3// You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) 2020, Olof Kraigher olof.kraigher@gmail.com
6use crate::ast::*;
7use crate::named_entity::*;
8
9use fnv::FnvHashMap;
10use std::collections::hash_map::Entry;
11
12#[derive(Clone, Debug)]
13struct VisibleEntity<'a> {
14    // The position where the entity was made visible
15    visible_pos: Vec<Option<SrcPos>>,
16    entity: EntRef<'a>,
17}
18
19#[derive(Debug)]
20pub enum ConflictingName {
21    MadeVisible,
22    Declared,
23}
24
25#[derive(Debug)]
26pub struct IntoUnambiguousError {
27    designator: Designator,
28    conflicting_names: Vec<(SrcPos, ConflictingName)>,
29}
30
31impl IntoUnambiguousError {
32    pub fn new(designator: Designator) -> IntoUnambiguousError {
33        IntoUnambiguousError {
34            designator,
35            conflicting_names: Vec::new(),
36        }
37    }
38
39    pub fn add_conflicting(&mut self, pos: SrcPos, name: ConflictingName) {
40        self.conflicting_names.push((pos, name))
41    }
42
43    pub fn into_diagnostic(self, ctx: &dyn TokenAccess, span: TokenSpan) -> Diagnostic {
44        let mut error = Diagnostic::new(
45            span.pos(ctx),
46            format!(
47                "Name '{}' is hidden by conflicting use clause",
48                self.designator
49            ),
50            ErrorCode::ConflictingUseClause,
51        );
52        for (pos, name) in self.conflicting_names {
53            let msg = match name {
54                ConflictingName::MadeVisible => {
55                    format!("Conflicting name '{}' made visible here", self.designator)
56                }
57                ConflictingName::Declared => {
58                    format!("Conflicting name '{}' declared here", self.designator)
59                }
60            };
61            error.add_related(pos, msg)
62        }
63        error
64    }
65}
66
67impl<'a> VisibleEntity<'a> {
68    fn clone_with_more_visiblity(&self, visible_pos: Option<&SrcPos>) -> VisibleEntity<'a> {
69        let mut more = self.clone();
70        more.visible_pos.push(visible_pos.cloned());
71        more
72    }
73}
74
75#[derive(Clone)]
76pub struct VisibleRegion<'a> {
77    // The position where the entity was made visible
78    visible_pos: Vec<Option<SrcPos>>,
79    region: &'a Region<'a>,
80}
81
82impl<'a> VisibleRegion<'a> {
83    /// Add more visiblity so that when using a context we can follow the visibility chain
84    fn clone_with_more_visiblity(&self, visible_pos: Option<&SrcPos>) -> VisibleRegion<'a> {
85        let mut more = self.clone();
86        more.visible_pos.push(visible_pos.cloned());
87        more
88    }
89
90    pub fn region(&self) -> &Region<'a> {
91        self.region
92    }
93}
94
95#[derive(Clone, Default)]
96pub struct Visibility<'a> {
97    // TODO store unique regions
98    all_in_regions: Vec<VisibleRegion<'a>>,
99    visible: FnvHashMap<Designator, FnvHashMap<EntityId, VisibleEntity<'a>>>,
100}
101
102impl<'a> Visibility<'a> {
103    pub fn make_all_potentially_visible(
104        &mut self,
105        visible_pos: Option<&SrcPos>,
106        region: &'a Region<'a>,
107    ) {
108        self.all_in_regions.push(VisibleRegion {
109            visible_pos: vec![visible_pos.cloned()],
110            region,
111        });
112    }
113
114    pub fn all_in_region(&self) -> impl Iterator<Item = &VisibleRegion<'a>> {
115        self.all_in_regions.iter()
116    }
117
118    pub fn visible(&self) -> impl Iterator<Item = EntRef<'a>> + '_ {
119        self.visible.values().flatten().map(|entry| entry.1.entity)
120    }
121
122    pub fn add_context_visibility(
123        &mut self,
124        visible_pos: Option<&SrcPos>,
125        visibility: &Visibility<'a>,
126    ) {
127        for visible_region in visibility.all_in_regions.iter() {
128            self.all_in_regions
129                .push(visible_region.clone_with_more_visiblity(visible_pos));
130        }
131
132        for (designator, visibile) in visibility.visible.iter() {
133            for visible_ent in visibile.values() {
134                // Implicit declarations will already have been added when used in the context
135                self.insert(
136                    designator.clone(),
137                    visible_ent.clone_with_more_visiblity(visible_pos),
138                );
139            }
140        }
141    }
142
143    pub fn make_potentially_visible_with_name(
144        &mut self,
145        visible_pos: Option<&SrcPos>,
146        designator: Designator,
147        ent: EntRef<'a>,
148    ) {
149        // Add implicit declarations when using declaration
150        // For example all enum literals are made implicitly visible when using an enum type
151        for entity in ent.as_actual().implicits.iter() {
152            self.make_potentially_visible_with_name(
153                visible_pos,
154                entity.designator().clone(),
155                entity,
156            );
157        }
158
159        let visible_ent = VisibleEntity {
160            visible_pos: vec![visible_pos.cloned()],
161            entity: ent,
162        };
163
164        self.insert(designator, visible_ent);
165    }
166
167    fn insert(&mut self, designator: Designator, visible_ent: VisibleEntity<'a>) {
168        match self.visible.entry(designator) {
169            Entry::Vacant(entry) => {
170                let mut map = FnvHashMap::default();
171                map.insert(visible_ent.entity.id(), visible_ent);
172                entry.insert(map);
173            }
174            Entry::Occupied(mut entry) => {
175                entry.get_mut().insert(visible_ent.entity.id(), visible_ent);
176            }
177        }
178    }
179
180    /// Helper function lookup a visible declaration within the region
181    pub fn lookup_into(&self, designator: &Designator, visible: &mut Visible<'a>) {
182        for visible_region in self.all_in_regions.iter() {
183            if let Some(named_entities) = visible_region.region.lookup_immediate(designator) {
184                match named_entities {
185                    NamedEntities::Single(entity) => {
186                        visible.insert(visible_region.visible_pos.clone(), entity);
187                    }
188                    NamedEntities::Overloaded(overloaded) => {
189                        for entity in overloaded.entities() {
190                            visible.insert(visible_region.visible_pos.clone(), entity.into());
191                        }
192                    }
193                }
194            }
195        }
196
197        if let Some(visible_entities) = self.visible.get(designator) {
198            for visible_entity in visible_entities.values() {
199                visible.insert(visible_entity.visible_pos.clone(), visible_entity.entity);
200            }
201        }
202    }
203}
204
205#[derive(Default, Debug)]
206pub struct Visible<'a> {
207    visible_entities: FnvHashMap<EntityId, VisibleEntity<'a>>,
208}
209
210impl<'a> Visible<'a> {
211    fn insert(&mut self, visible_pos: Vec<Option<SrcPos>>, entity: EntRef<'a>) {
212        let actual_entity = entity.as_actual();
213
214        match self.visible_entities.entry(actual_entity.id()) {
215            Entry::Vacant(entry) => {
216                entry.insert(VisibleEntity {
217                    visible_pos,
218                    entity,
219                });
220            }
221            Entry::Occupied(mut entry) => {
222                let old_entity = &entry.get().entity;
223                if entity.is_alias_of(old_entity) {
224                    // Ensure deepest alias is made visible
225                    entry.insert(VisibleEntity {
226                        visible_pos,
227                        entity,
228                    });
229                }
230            }
231        };
232    }
233
234    pub fn into_unambiguous(
235        self,
236        designator: &Designator,
237    ) -> Result<Option<NamedEntities<'a>>, IntoUnambiguousError> {
238        let mut named_entities: Vec<_> = self
239            .visible_entities
240            .values()
241            .map(|ent| ent.entity)
242            .collect();
243
244        if named_entities.is_empty() {
245            Ok(None)
246        } else if named_entities.iter().all(|ent| ent.is_overloaded()) {
247            Ok(Some(NamedEntities::new_overloaded(
248                named_entities
249                    .into_iter()
250                    .map(|ent| OverloadedEnt::from_any(ent).unwrap())
251                    .collect(),
252            )))
253        } else if named_entities.len() == 1 {
254            Ok(Some(NamedEntities::new(named_entities.pop().unwrap())))
255        } else {
256            let mut error = IntoUnambiguousError::new(designator.clone());
257            // Duplicate visible items hide each other
258
259            fn last_visible_pos(visible_entity: &VisibleEntity<'_>) -> u32 {
260                if let Some(pos) = visible_entity.visible_pos.iter().rev().flatten().next() {
261                    return pos.range().start.line;
262                }
263                0
264            }
265
266            // Sort by last visible pos to make error messages and testing deterministic
267            let mut visible_entities: Vec<_> = self.visible_entities.values().collect();
268            visible_entities.sort_by_key(|ent| last_visible_pos(ent));
269
270            for visible_entity in visible_entities {
271                for visible_pos in visible_entity.visible_pos.iter().rev().flatten() {
272                    error.add_conflicting(visible_pos.clone(), ConflictingName::MadeVisible);
273                }
274                if let Some(pos) = visible_entity.entity.decl_pos() {
275                    error.add_conflicting(pos.clone(), ConflictingName::Declared);
276                }
277            }
278
279            Err(error)
280        }
281    }
282}