Refactor SSR so that placeholders store a Var

This allows lookup of placeholder bindings given a placeholder without
needing to create a Var instance.
This commit is contained in:
David Lattimore 2020-08-06 11:30:52 +10:00
parent 674af600f1
commit c84f98385a
4 changed files with 26 additions and 18 deletions

View file

@ -2,7 +2,7 @@
//! process of matching, placeholder values are recorded. //! process of matching, placeholder values are recorded.
use crate::{ use crate::{
parsing::{Constraint, NodeKind, Placeholder}, parsing::{Constraint, NodeKind, Placeholder, Var},
resolving::{ResolvedPattern, ResolvedRule, UfcsCallInfo}, resolving::{ResolvedPattern, ResolvedRule, UfcsCallInfo},
SsrMatches, SsrMatches,
}; };
@ -56,10 +56,6 @@ pub struct Match {
pub(crate) rendered_template_paths: FxHashMap<SyntaxNode, hir::ModPath>, pub(crate) rendered_template_paths: FxHashMap<SyntaxNode, hir::ModPath>,
} }
/// Represents a `$var` in an SSR query.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct Var(pub String);
/// Information about a placeholder bound in a match. /// Information about a placeholder bound in a match.
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct PlaceholderMatch { pub(crate) struct PlaceholderMatch {
@ -182,10 +178,9 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
// We validated the range for the node when we started the match, so the placeholder // We validated the range for the node when we started the match, so the placeholder
// probably can't fail range validation, but just to be safe... // probably can't fail range validation, but just to be safe...
self.validate_range(&original_range)?; self.validate_range(&original_range)?;
matches_out.placeholder_values.insert( matches_out
Var(placeholder.ident.to_string()), .placeholder_values
PlaceholderMatch::new(code, original_range), .insert(placeholder.ident.clone(), PlaceholderMatch::new(code, original_range));
);
} }
return Ok(()); return Ok(());
} }
@ -487,7 +482,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
} }
if let Phase::Second(match_out) = phase { if let Phase::Second(match_out) = phase {
match_out.placeholder_values.insert( match_out.placeholder_values.insert(
Var(placeholder.ident.to_string()), placeholder.ident.clone(),
PlaceholderMatch::from_range(FileRange { PlaceholderMatch::from_range(FileRange {
file_id: self.sema.original_range(code).file_id, file_id: self.sema.original_range(code).file_id,
range: first_matched_token range: first_matched_token

View file

@ -8,7 +8,7 @@
use crate::errors::bail; use crate::errors::bail;
use crate::{SsrError, SsrPattern, SsrRule}; use crate::{SsrError, SsrPattern, SsrRule};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use std::str::FromStr; use std::{fmt::Display, str::FromStr};
use syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, T}; use syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, T};
use test_utils::mark; use test_utils::mark;
@ -34,12 +34,16 @@ pub(crate) enum PatternElement {
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Placeholder { pub(crate) struct Placeholder {
/// The name of this placeholder. e.g. for "$a", this would be "a" /// The name of this placeholder. e.g. for "$a", this would be "a"
pub(crate) ident: SmolStr, pub(crate) ident: Var,
/// A unique name used in place of this placeholder when we parse the pattern as Rust code. /// A unique name used in place of this placeholder when we parse the pattern as Rust code.
stand_in_name: String, stand_in_name: String,
pub(crate) constraints: Vec<Constraint>, pub(crate) constraints: Vec<Constraint>,
} }
/// Represents a `$var` in an SSR query.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct Var(pub String);
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum Constraint { pub(crate) enum Constraint {
Kind(NodeKind), Kind(NodeKind),
@ -205,7 +209,7 @@ fn parse_pattern(pattern_str: &str) -> Result<Vec<PatternElement>, SsrError> {
if token.kind == T![$] { if token.kind == T![$] {
let placeholder = parse_placeholder(&mut tokens)?; let placeholder = parse_placeholder(&mut tokens)?;
if !placeholder_names.insert(placeholder.ident.clone()) { if !placeholder_names.insert(placeholder.ident.clone()) {
bail!("Name `{}` repeats more than once", placeholder.ident); bail!("Placeholder `{}` repeats more than once", placeholder.ident);
} }
res.push(PatternElement::Placeholder(placeholder)); res.push(PatternElement::Placeholder(placeholder));
} else { } else {
@ -228,7 +232,7 @@ fn validate_rule(rule: &SsrRule) -> Result<(), SsrError> {
for p in &rule.template.tokens { for p in &rule.template.tokens {
if let PatternElement::Placeholder(placeholder) = p { if let PatternElement::Placeholder(placeholder) = p {
if !defined_placeholders.contains(&placeholder.ident) { if !defined_placeholders.contains(&placeholder.ident) {
undefined.push(format!("${}", placeholder.ident)); undefined.push(placeholder.ident.to_string());
} }
if !placeholder.constraints.is_empty() { if !placeholder.constraints.is_empty() {
bail!("Replacement placeholders cannot have constraints"); bail!("Replacement placeholders cannot have constraints");
@ -344,7 +348,17 @@ impl NodeKind {
impl Placeholder { impl Placeholder {
fn new(name: SmolStr, constraints: Vec<Constraint>) -> Self { fn new(name: SmolStr, constraints: Vec<Constraint>) -> Self {
Self { stand_in_name: format!("__placeholder_{}", name), constraints, ident: name } Self {
stand_in_name: format!("__placeholder_{}", name),
constraints,
ident: Var(name.to_string()),
}
}
}
impl Display for Var {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "${}", self.0)
} }
} }

View file

@ -1,6 +1,5 @@
//! Code for applying replacement templates for matches that have previously been found. //! Code for applying replacement templates for matches that have previously been found.
use crate::matching::Var;
use crate::{resolving::ResolvedRule, Match, SsrMatches}; use crate::{resolving::ResolvedRule, Match, SsrMatches};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use syntax::ast::{self, AstToken}; use syntax::ast::{self, AstToken};
@ -114,7 +113,7 @@ impl ReplacementRenderer<'_> {
fn render_token(&mut self, token: &SyntaxToken) { fn render_token(&mut self, token: &SyntaxToken) {
if let Some(placeholder) = self.rule.get_placeholder(&token) { if let Some(placeholder) = self.rule.get_placeholder(&token) {
if let Some(placeholder_value) = if let Some(placeholder_value) =
self.match_info.placeholder_values.get(&Var(placeholder.ident.to_string())) self.match_info.placeholder_values.get(&placeholder.ident)
{ {
let range = &placeholder_value.range.range; let range = &placeholder_value.range.range;
let mut matched_text = let mut matched_text =

View file

@ -31,7 +31,7 @@ fn parser_two_delimiters() {
fn parser_repeated_name() { fn parser_repeated_name() {
assert_eq!( assert_eq!(
parse_error_text("foo($a, $a) ==>>"), parse_error_text("foo($a, $a) ==>>"),
"Parse error: Name `a` repeats more than once" "Parse error: Placeholder `$a` repeats more than once"
); );
} }